1. 程式人生 > >JAVA學習筆記及知識積累

JAVA學習筆記及知識積累

 為什麼說Java具有跨平臺特性?

我們知道計算機只認識1,0兩種電平的訊號,所有資訊或者計算指令最終都編碼成16進位制的機器碼,這些機器碼作為程式保存於計算機的記憶體中,由CPU去單個取指令執行直到程式執行完畢。然而計算機能認識的這些機器碼確實不是人類善於處理的,因此人們發明了組合語言,隨後使用匯編器(assembler)翻譯成為機器碼;再隨後貝爾實驗室發明了C語言,這個就是人類能夠理解並創造的高階程式了。同樣地,要在CPU上執行,我們必須翻譯成機器碼,這個由編譯器來完成。我們來看下面一句程式:

printf(1+2);

在x86 intel機器上翻譯成機器碼就是 101000101010 ,intel x86處理器執行這個程式碼,其中也包含了系統呼叫的機器碼

,在這裡有兩個最關鍵的東西:1.intel處理器,2.作業系統。這兩者的組合我們就稱為platform.顯然對應不同的處理器和不同的作業系統會有不同的排列組合。對於我們應用開發者來說,我們當然希望我們的應用程式可以很方便地在不同平臺上執行。那我們來看看,有哪些方案:

1. 針對不同的處理器彙編問題,我們需要購買host在windows下的針對不同target cpu的彙編器負責轉換出能夠target到不同cpu的機器碼;這個過程叫做交叉編譯。並且不同的cpu輸出不同的機器碼檔案,不能混用

2. 針對不同的作業系統,我們printf做的系統呼叫也會有所不同

但是我們要知道的是交叉編譯並且針對不同OS特性做不同的適配,本身就是非常複雜昂貴的,不是每個高階語言開發工程師都能勝任的。這就有了java出現的機會,我們來看看java平臺是如何解決這個問題的。

java平臺中有一個非常重要的模組: Java Virtual Machine,所有預編譯過的bytecode就在這個虛擬機器上執行。詳細過程如下:

1. printf(1+2)這個程式碼用java就是System.out.println(1+2),該文字儲存為.java檔案;

2.使用java編譯器將這個.java檔案轉換成稱為bytecode的中間程式碼,輸出為.class檔案;

3.這個.class內容本身並不和任何特定平臺相關,也就是說任何平臺本身是無法直接執行的;

4.這個虛擬機器駐留在作業系統的記憶體中,當虛擬機器被喂入這些bytecode時,jvm會識別到我們正在工作的具體平臺並且將bytecode最終轉換為native machine code

這樣你的程式碼只要編譯一次,就能在所有平臺上執行!!

因此, java既是一個程式語言,更是一個平臺。

其他的程式語言,比如C語言,編譯器產生targeted到特定平臺的機器碼,比如wintel,比如linux+intel等,而java compiler只會將源程式編譯target到Java Virtual Machine. Bytecode是host system和java source的橋樑

Maven是什麼以及Maven,IDE,Mark,Ant對比

Maven是一個java的構建工具,類似於C語言的make,同時Maven也是一個依賴管理的工具。

In short, Archetype is a Maven project templating toolkit. An archetype is defined as an original pattern or model from which all other things of the same kind are made. The name fits as we are trying to provide a system that provides a consistent means of generating Maven projects. Archetype will help authors create Maven project templates for users, and provides users with the means to generate parameterized versions of those project templates.

Maven archetype

maven提供了很多工程模版,當建立一個新專案時,就可以使用這些模版,自動建立配置好對應的環境

IDE對比:

Eclipse, IntelliJ Idea, myEclips等,我比較喜歡Idea,特別地,java for android新的正式工具也是基於idea設計的。寫程式碼超級爽.使用Idea可以開啟一個j2ee hello world程式來學習

JAVA SE/Java EE/ Java ME/JavaFX/Java Card/Java TV/Java DB

java主要分為3大平臺:

java SE (J2SE)= standard edition:這是核心的java程式設計平臺,包含了java.lang,java.io,java.math,java.net,java.util等通用的庫和API。主要用於桌面應用開發

java ee (J2EE)= enterprise edition: 在SE基礎上,增加了用於部署高容錯,分散式,多層級的java軟體的庫,這些基本上都以模組化的元件模式執行在application server上。也就是說,如果你的應用會形成巨大規模,分散式的系統,那麼你應該考慮JAVA EE。它還提供包括資料庫訪問(JDBC,JPA),遠端函式呼叫RMI,訊息(JMI),web services, XML解析,並且定義了企業級的JavaBeans, servlets, portlets, Java Server Pages等標準API。主要用於web(網路)應用開發

java me(J2ME) = mico edition.用於開發mobile device上的應用,嵌入於類似機頂盒的裝置中。主要用於手機應用

jdk變遷歷史

JAVA程式的編譯和執行過程

JAVA執行環境(JRE)

JRE = Java Runtime Environment = JVM + API(Lib)

JRE執行程式時的三項主要功能:由class loader來載入程式碼,由bytecode verifier來校驗程式碼,由runtime interpreter來執行程式碼

一句話:由虛擬機器來裝載編譯好的應用程式並且呼叫相應的指令具體地執行。虛擬機器可以理解為在實際的wintel, linux/intel平臺上虛擬出一個新的機器,有他自己的指令系統,作業系統API介面,對下會匹配到不同的平臺,對上展示的介面是相同的,因此具有跨平臺的特徵

JAVA開發工具包(JDK)

JDK = JRE+ Tools = JVM + API + Tools 

JDK提供以下主要的開發工具:

  • java編譯器: javac.exe
  • java執行器: java.exe
  • 文件生成器: javadoc.exe
  • java打包器: jar.exe
  • java偵錯程式:jdb.exe
  • javaw:執行圖形介面程式,並且不啟控制檯
  • javap:檢視類資訊及反彙編

javap反彙編後形成的jvm位元組碼指令和源程式的對應關係:

public class Main {

    public static void main(String[] args) {
        System.out.println("hello, world");
    }
}

J:\eclipse-workspace\TType>javap -c out\production\TType\Main.class
Compiled from "Main.java"
public class Main {
  public Main();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #3                  // String hello, world
       5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: return
}

面向物件設計的要點:

  • 將客觀世界中的各種物件對映為程式中的一個物件
  • 程式的分析和設計都圍繞著:
    • 分析有哪些物件類
    • 每個類都有哪些屬性,哪些方法
    • 類之間的關係:繼承關係和關聯關係(比如人有一個手錶,老師屬於一個學校,屬於一個院系)
    • 物件之間互相呼叫

JAVA Application .VS. Applet

兩者的執行環境不同,application是獨立的程式,需要執行器(呼叫虛擬機器)來執行;而applet則是嵌在HTML頁面中的非獨立程式,由專門的appletViewer來執行,或者由web瀏覽器呼叫JAVA虛擬機器來執行.applet是java最早提出的,是對網頁web技術發展的重大改進,從此網頁變得豐富,可互動了。要在網頁中執行applet必須在系統中有JRE安裝,並且要在瀏覽器中使能java,我們需要將.class以及.html檔案放到www伺服器上,然後用瀏覽器訪問。從java8開始, applet的執行就收到更加嚴格的限制。

applet替代方案有flash, silverlight等。甚至現在隨著javascript富體驗方案以及HTML5提供的豐富功能引入,applet已經慢慢退出歷史舞臺.

.jar包

.jar是一系列java的類位元組檔案打包生成的壓縮檔案,jar包伴隨一個非常重要的是清單檔案(mainifest),指出這個jar包的入口類是哪一個

要使用jar包一般經過:編譯->打包->執行三個階段

javac A.java
jar cvfm A.jar manifest.mf A.class
java -jar A.jar

 輸入與輸出

application輸入輸出可以是文字介面,也可以圖形介面,而applet則只能是圖形介面

文字介面的輸入輸出: java.util.Scanner類

圖形介面輸入輸出:初始化一個frame,在這個frame中add一些控制元件,控制元件中新增事件處理

Java資料型別劃分

基本資料型別:byte,short,int,long,float,double,char,boolean存在棧裡面

引用資料型別:class,interface,陣列:資料本身存在堆裡面,但是指標存在棧裡面

記憶體區間之常量池StringBuffer

看下面的程式碼, 由於常量是存在方法區中的常量池中,值相等的常量就是同一個常量,而字串"aaa"本質上就是一個“常量”.但是如果通過new String方式建立的字串則存在於系統堆中,區域性變數僅僅是指向了堆中分配的記憶體而已。由於字串本質上是一個常量,而我們經常需要做字串拼接賦值,每一次這類操作都會產生一個字串副本,效率低下。為解決這個問題,需要使用StringBuffer,也就是可以變更的string。

public static void main(String[] args) {
        String s1= "aaa";
        String s2="aaa";
        System.out.println(s1==s2); // true
        String s3 = new String("aaa");
        String s4 = new String("aaa");
        System.out.println(s3==s4); // false
        System.out.println(s3.equals(s4)); // true
        System.out.println(s3==s2);// false

        String s5 = "aaa";
        String s6 = s5;
        s6 = s6 + "bbb";
        // 字串是不可變的,每次修改本質上都是建立了一個副本
        System.out.println(s5==s6); // false
        StringBuffer s7 = new StringBuffer("aaa");
        StringBuffer s8 = s7;
        s7.append("bbb");
        System.out.println(s7==s8); // true
    }

package

package實際上為了解決名字空間,名字衝突,類似php的名稱空間,需要和對應儲存路徑相對應

package pkg1[.pkg2]

根目錄由classpath環境變數來確定,如果沒有package語句,則是default package.

java jdk實際上提供了很多包,比如: java.applet,java.awt,java.awt.image,java.net,java.util等

import語句

為了使用java中提供的類,需要用import語句來匯入所需要的類。

import java.util.Date

匯入類後就可以直接使用Date而不用加package的字首了。

package和物理目錄之間的關係

http://www.cnblogs.com/moveofgod/p/3809653.html

package和jar包

第三方的Package一般都以jar包形式來發布。我們知道jar包實際上是包含了一堆類位元組碼的壓縮包。我們要在專案中使用,首先得引入jar包,並將對應jar包加到對應的build path,其本質是classpath加入jar包,這樣在java程式碼中import 第三方jar包中的類時,jvm就能找到對應的類位元組碼並載入執行。

https://blog.csdn.net/zhenyusoso/article/details/6174834

編譯和執行包中的類

javac -d . pk\*.java
java pk.TestPkg  /*pk是package, TestPkg是包含main的class*/

java訪問許可權控制修飾符: public/protected/private/預設

需要注意的是,預設如果沒有任何修飾符,則成員在同一個包中可以訪問.首先要看是否類為public,也就是說是否可以在其他package中能夠訪問該類,只有有權訪問類,再談能否訪問該類的方法。

public class PubClass{
defaultFunction(){} // 預設可以被包內訪問
privateFunction(){} // 只能在本類中訪問
protectedFunction(){} // 在子類中也能夠訪問
publicFunction(){} // 任何地方都能夠訪問
}

private成員的setter/getter(setAttribute/getAttribute)

對於私有的成員,我們一般通過提供setter/getter函式提供讀和/或寫,好處是能夠做合法檢查,做量剛的變換等。。如果不提供setAttribute方法則該屬性就是隻讀的

static/final/abstract修飾符

非常常見的,比如System.in和System.out其中in和out就是在System這個類中定義的static變數,因此任何時候都可以訪問。

static如修飾方法,則該方法屬於類,不被任何類所專有,在呼叫該static方法時,無須事先例項化一個物件

同樣,如果沒有static關鍵字則方法會屬於特定的物件。由於static方法屬於類,而不屬於物件,因此static方法不能操作屬於類例項的成員變數!static方法不能使用this或super

final類表示不可被繼承,final方法表示不可被子類覆蓋overwrite的方法,final欄位表示一旦初始化就不可改變!

static final修飾某個欄位時,該欄位為常量,比如Math.PI, integer.MAX_VALUE都是static final型別的成員變數

abstract方法必須在子類中實現, abstract類不能被初始化

介面(interface)

介面就是某種特殊的約定,介面可以被多個類來實現,軟體工程趨勢於:面向介面的程式設計,而不是面向實現。

通過介面可以實現不相關的類的相同的行為,而無需考慮這些類之間的層次關係。某種意義上實現了多重繼承

介面和類的繼承層次無關,同一個介面可以被不同的類來實現。

比如上面這個圖中: Flyable這個介面定義了takeoff,fly,land三個介面方法,該介面被Airplane, Bird, Superman三個類來implement了,但是這三個類分別繼承於Vehicle, Animal這兩個抽象類(abstract class)

JAVA 8中實現了介面函式的預設實現,這樣有點像似繼承了,不用每個申明implements這個介面的類中都要實現每一個函式。

類成員欄位變數和區域性變數

欄位變數為物件的一部分,因此存在於物件中,也就是堆中;

區域性變數為成員函式內宣告的變數,存在於棧中

生命週期不同:欄位變數隨物件建立後一直存在直到物件銷燬,而區域性變數只有在函式被呼叫時存在,呼叫結束則釋放記憶體;

欄位變數自動賦初值為0, 區域性變數則不會自動初始化,必須顯式地初始化。

函式呼叫時變數的傳遞(值傳遞和引用傳遞)

總的來說,呼叫物件方法時,java是值傳遞,即:將表示式的值複製給形式引數。對於引用型變數,傳遞的值是引用值,不會複製物件實體本身

多型(polymorphism)

多型是指一個程式中相同的名字表示不同的含義。java的多型有兩種情形:

  • 編譯時多型:過載(overload)是多個同名但是不同簽名(引數不同)的不同方法,如:p.sayHello(),p.sayHello("zhang");
  • 執行時多型
    • 覆蓋override:子類對父類方法進行覆蓋,通過動態繫結(dynamic binding),也稱之為虛方法呼叫(virtual method invoking),因為程式呼叫的是虛的,只有在執行時系統根據呼叫該方法的例項的型別來決定呼叫哪個方法
    • 在呼叫方法時,程式會正確地呼叫子類物件的方法
public class Main {
    static void  callDraw(Shape s){
        s.draw();
    }
    public static void main(String[] args) {
        Circle c = new Circle();
        Triangle t = new Triangle();
        Line l = new Line();
        callDraw(c);
        callDraw(t);
        callDraw(l);
    }
}
class Shape{
    void draw(){        System.out.println("shape drawing");}
}
class Circle extends Shape{
    void draw(){        System.out.println("Circle drawing");}
}
class Triangle extends  Shape{
    void draw(){       System.out.println("triangle drawing");}
}
class Line extends Shape{
    void draw(){       System.out.println("line drawing");}
}

上面這個例子中到底呼叫的是哪個draw則在執行時決定。

虛方法呼叫和非虛呼叫

java中,普通的方法就是虛方法,但是以下幾種情形不是虛方法呼叫:

static/private/final

請注意下面的例子中,由於Shape中的draw為static也就是說屬於類的,就不會觸發虛方法呼叫,而輸出3個相同的shape drawing。這時的呼叫依賴於申明的類,這裡就是Shape類,和傳入的是circle,triangle等無關了

public class Main {

    static void  callDraw(Shape s){
        s.draw();
    }
    public static void main(String[] args) {
        Circle c = new Circle();
        Triangle t = new Triangle();
        Line l = new Line();
        callDraw(c);
        callDraw(t);
        callDraw(l);
    }
}
class Shape{
    static void draw(){ System.out.println("shape drawing");}
}
class Circle extends Shape{
    static void draw(){System.out.println("Circle drawing");}
}
class Triangle extends  Shape{
    static void draw(){System.out.println("triangle drawing");}
}
class Line extends Shape{
    static void draw(){System.out.println("line drawing");}
}
// shape drawing
// shape drawing
// shape drawing

建構函式的層級呼叫

在建立一個物件時,如果類中沒有建構函式,則系統會自動呼叫super,這時一定要注意父類中的建構函式必須是無引數的函式,否則就會出錯。java編譯器的原則是必須令所有父類的構造方法都能得到呼叫

因此,如果不顯式地呼叫super,則必須保證其父類中的建構函式為無引數,否則編譯出錯 

例項初始化與靜態初始化

public class Main {
    public static void main(String[] args) {
        InitialTest2 init2 = new InitialTest2(2);
    }
}
class InitialTest{
    static int x=0;
    static {
        x++;
        System.out.println("static..."+x);
    }
}
class InitialTest2 extends  InitialTest{
    InitialTest2(int a){
        this.a = a;
        System.out.println("consturction2 : this.a="+a);
    }
    int a;
    {
        System.out.println("IntialTest2 before instance created..."+this.a);
    }
    static {
        x++;
        System.out.println("static2 init..."+x);
    }
}
/* 輸出結果:
static...1
static2 init...2
IntialTest2 before instance created...0
consturction2 : this.a=2
*

由於通過super呼叫在construct中可能會在呼叫虛方法時繞回到子類中訪問未初始化的資料,因此儘量不要在建構函式中呼叫方法,如果必須呼叫的話就呼叫final方法

物件清除(garbage collection)

System.gc()呼叫僅僅建議啟動系統的垃圾回收,但是並不能真正做到垃圾的回收。

也可以在子類的finalize()過載實現中去釋放資源,類似c++的解構函式。

對於實現了java.lang.AutoCloseable的物件,我們可以使用try正規化在程式碼執行完畢後自動關閉資源:

        try(Scanner scanner = new Scanner(...)) {
            ...
        }

類的類、匿名類

匿名類沒有類名,在定義類的同時就生成該物件的一個例項,一次性使用

lambda表示式

相當於匿名函式

(引數)->結果

的形式,類似於javascript的匿名函式

@override是幹嘛的

@override是java的虛擬碼,表示在這裡要重寫下面的方法,當然也可以沒有。寫了這個虛擬碼的好處:

1、可以當註釋用,方便閱讀;
2、編譯器可以給你驗證@Override下面的方法名是否是你父類中所有的,如果沒有則報錯。例如,你如果沒寫@Override,而你下面的方法名又寫錯了,這時你的編譯器是可以編譯通過的,因為編譯器以為這個方法是你的子類中自己增加的方法。

注意過載和重寫的區別,過載是方法的引數個數或者引數型別不同,導致是不同的方法。而重寫是子類對父類相同方法的覆蓋重寫

POJO(Plain Old Java Object)

POJO = Plain Old Java Object字面意思是普通java物件。其內在含義為沒有繼承任何類的普通類例項物件

JavaBean

一個pojo類(plain old java object)物件中,如果其欄位都有對應的getter和setter,並且有一個無參的建構函式,這類物件我們稱之為JavaBean,JavaBean往往由容器來建立,比如tomcat建立javabean,因此需要保證有一個無參建構函式。更多用於資料的臨時中轉

war檔案

war檔案是一個適用於tomcat webapp目錄下部署的web專案包檔案。通常使用一下命令打包和檢視,通常包含一堆的jar包檔案

jar -cvf blog.war * // 打包
jar -tf blog.war //檢視

範型Point<T>

有的時候,具體使用什麼class的引數,只有在使用時才能確定,那麼比較好的方案就是使用範型。比如要設計一個Point類,其成員變數可能可以使用整形,也可以使用浮點數,那麼就可以使用:

public class Main {

    public static void main(String[] args) {
        Point<String> p1 = new Point<String>(); // 在呼叫處指定對應型別
        p1.x = "20";
        p1.y = "24";
        System.out.println(p1.x);
    }
}
class Point<T>{
    T x;
    T y;
}

JVM exception處理

java程式執行時出錯的話要麼自己try{}catch主動處理,要麼通過throws呼叫交由jvm自行處理。java程式也可以主動丟擲異常,層層向上。

throw vs throws

throw在方法體內,由java自己主動丟擲異常,而throws則在方法後面緊跟本方法可能丟擲的異常,而在別人呼叫這個異常時,就必須實現catch相應的異常,或者再次主動丟擲異常。

反射

類載入器

類載入器負責將.class位元組碼檔案載入到記憶體中,併為之生成對應的class物件。以便後續使用。

  • 類載入器分以下幾類:根類載入器,也稱為引導類載入器,負責java核心類的載入,比如system, string等,在JRE的lib目錄下rt.jar檔案中;
  • 擴充套件類載入器,負責JRE擴充套件目錄中jar包的載入,在jre lib目錄下的ext目錄
  • 系統類載入器,負責在JVM啟動時載入來自java命令的class檔案以及在classpath環境變數中所指定的jar包和類路徑

什麼是反射

java反射機制是在執行狀態中,對於任何一個類,都能夠知道這個類的所有屬性和方法;對於任意一個物件,都能夠呼叫它的任意一個方法和屬性;這種動態獲取物件的資訊以及動態呼叫物件方法的功能稱為java語言的反射機制。

要使用反射,就必須獲得位元組碼檔案

Class.forName/obj.getClass獲取位元組碼

public class Main {

    public static void main(String[] args) throws ClassNotFoundException {
        Class pclass1 = Class.forName("Point");
        Class pclass2 = Point.class;
        Point pobj = new Point();
        Class pclass3 = pobj.getClass();
        System.out.println(pclass1==pclass2); // true
        System.out.println(pclass2 == pclass3); // true
    }
}

以上說明幾種方式獲取class位元組碼都是相同的拷貝,其中如果使用idea則隨時可能要使用alt+enter快捷鍵增加一個local變數來引用返回值,及快捷增加exception處理。

 使用反射暴力修改private成員

public class Main {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException {
        Class pClass = Class.forName("Point");
        Point pObj = (Point)pClass.newInstance();
        Field privateVarField = pClass.getDeclaredField("privateVar");
        privateVarField.setAccessible(true);
        privateVarField.set(pObj,205);
        System.out.println(pObj.getPrivateVar()); // 私有變數已被修改為205 
    }
}

使用反射暴力修改private方法訪問許可權

class Point{
    private int privateVar = 1;
    int x;
    int y = 2;

    private int privateGetX(){
        return  x;
    }
    public int setX(int a){
        x = a;
        return 0;
    }
    public int publicGetY() {
        return y;
    }
}
public class Main {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException, NoSuchMethodException, InvocationTargetException {
        Class pClass = Class.forName("Point");
        Point pObj = (Point)pClass.newInstance();
        pObj.setX(5);
        Method publicGetY = pClass.getMethod("publicGetY");
        System.out.println(publicGetY.invoke(pObj)); // 2
        Method privateGetX = pClass.getDeclaredMethod("privateGetX");
        // 暴力破解private Method,使得可以通過反射來呼叫
        privateGetX.setAccessible(true);
        System.out.println(privateGetX.invoke(pObj)); // 5
    }
}

JAVA內部類

在java中,一個類內部又可以定義內部類,內部類有以下幾種存在形式

1.成員內部類

class OutClass{
    private int num = 3;
    class Inner { // 成員內部類
        int num = 4;
        void show(){
            int num = 5;
            System.out.println("num" + num); // num:5
            System.out.println("num" + this.num); // num:4 本內部類的成員
            System.out.println("num" + OutClass.this.num); // num:3 外部類的成員訪問
        }
    }
    public static void main(String[] args){
        OutClass.Inner inner = new OutClass().new Inner(); // 由於有內部成員類,因此有了new方法,須先new一個外部類例項,再new內部類
        inner.show();
    }
}

2.靜態內部類

class OutClass{
    private static int num = 3;
    static class Inner { // 成員內部類
        int num = 4;
        void show(){
            int num = 5;
            System.out.println("num" + num); // num:5
            System.out.println("num" + this.num); // num:4 本內部類的成員
            System.out.println("num" + OutClass.num); // num:3只能訪問外部類靜態成員
        }
        static void showstatic(){
            int num = 5;
            System.out.println("num" + num); // num:5
            System.out.println("num" + OutClass.num); // num:3 外部類的成員訪問
        }
    }
    public static void main(String[] args){
        OutClass.Inner inner = new OutClass.Inner();
        inner.show();
        OutClass.Inner.showstatic();
    }
}

3.匿名內部類

匿名內部類往往用於從介面定義一個類,僅僅一次使用,沒有必要給他一個命名的情形

4.區域性內部類

Tomcat體系結構

tomcat作為server run time environment,以web容器的方式提供服務,包括connector(http,https,ajp及其他請求方式)的接入引擎,服務引擎,Host主機服務及host下面的context project等部分組成。

JAVAWeb分層結構

web層負責http請求的接收和響應;web層適用於MVC設計模式,controller實際上就是servlet,view則是JSP,model對於C操作,由input引數建立對應的model並經由service呼叫DAO持久化,對於R操作,則反過來由DAO層獲取到資料呈現到response中.

service層負責核心的業務邏輯;

Dao層則唯一負責和資料庫CRUD操作並以model方式返回資料

J2EE web專案執行包體系

JAVA多執行緒

java執行緒在呼叫start方法後將進入就緒狀態佇列,該佇列為一個先入先出的FIFO佇列,CPU會根據一定的排程演算法從該佇列中取出一個執行緒分配時間片進入執行狀態。

在執行過程中,如果時間片到時則會被搶佔進入就緒態,等待下一次排程;如果執行中需要等待某一資源,則阻塞自己進入等待態。執行緒執行完畢則銷燬態。

class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("當前執行緒名稱為:"+ Thread.currentThread().getName());
    }
}
public class ThreadDemo{
    public static void main(String[] args){
        MyThread myThread1 = new MyThread();
        MyThread myThread2 = new MyThread();
        myThread1.start(); // 分別列印   當前執行緒名稱為:Thread-0,注意順序可能是隨機的哦
        myThread2.start();  // 分別列印   當前執行緒名稱為:Thread-1
    }
}

  

tomcat http請求處理及響應

Servlet

servlet是執行在服務端的java程式段,遵循sun公司提供的一套規範介面。該介面中最重要的是service方法,還有init,destroy等介面。主要用來負責處理客戶端的請求並響應給瀏覽器以動態資源。servlet的實質就是java程式碼,通過java提供的API來動態向客戶端輸出內容。

需要注意的是,在java web以及junit開發中,我們不需要再寫main函式,因為該main函式在javaweb開發時是由tomcat的bootstrap類來提供的,是一個無限迴圈,永遠不會撤銷。而在junit中則在junit的框架程式碼中。我們的servlet程式由tomcat的main函式在接收到http請求時通過反射機制來具體呼叫servlet的位元組程式碼,並最終返回響應。servlet是連線web前端和後端邏輯的橋樑。servlet是singleton,因此在servlet中不能儲存使用者相關的資料(相當於是全域性互斥資料,會導致執行緒衝突),否則會導致混亂,注意執行緒安全

servlet的建立和對映:

可以通過手工在web.xml中定義,或者通過註解@WebServlet("/myservnet")的方式來指示編譯器完成對映。

單例項,多執行緒servlet呼叫模型

servlet配置過程,類似於PHP Laravel的路由配置過程,主要要指定對應的url-pattern,以及對應的servlet類。配置時也存在優先順序及覆蓋的問題。我們可以在站點級全域性web.xml中配置公共路由,也可以在專案級別建立web.xml實現專案級別的路由。

tomcat中靜態資源載入過程

當path後面寫的是靜態資源名稱,比如index.html,tomcat也會去找url-pattern有沒有可以匹配的內容,如果有,就載入對應servnet,如果沒有就找到配置中的預設url-pattern.

如果當前project沒有預設url-pattern,則找到站點級別的web.xml的預設匹配的url-pattern,一般在default servlet name項中

<servlet>
        <servlet-name>default</servlet-name>
        <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
        <init-param>
            <param-name>debug</param-name>
            <param-value>0</param-value>
        </init-param>
        <init-param>
            <param-name>listings</param-name>
            <param-value>false</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

JSP的本質

JSP本身實質上是一段將來被編譯為位元組碼的java程式,也是一個servlet,類似於PHP的模版引擎(xxx.blade.php),最終編譯出來的位元組碼(php native原始碼)完成的工作實際上就是拼接java資料到對應前端html中.

servlet的forward和redirect

當tomcat收到一個http請求時,路由到servlet,對應的servlet根據業務邏輯可能需要forward到其他servlet(這是內部轉移)或者直接返回一個重定向讓瀏覽器做redirect操作,最終才能完成業務。

如果需要servlet共享資料給jsp,則需要使用forward轉發,轉發只能轉到內部的資源。

往往通過request物件setAttribute增加一個數據,然後forward給jsp來顯示資料

request.setAttribute('productList',proList);
RequestDispatcher requestDispatcher = request.getRequestDispatcher('/product/query.jsp');
requestDispatcher.forward(request,response);

JSTL/EL/OGNL(Struts2)

JSTL/EL是用於簡化jsp編寫而定義的java規範,JSTL(tld)定義了一些描述性的標籤,EL則定義了一種以${ java程式碼 }的方式便於在jsp中執行java程式碼獲取資料,並且使用類似php的blade模版來表達展示資料

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<html>
  <head>
    <title>查詢商品</title>
  </head>
  <body>
  <c:forEach items="${requestScope.prodList}" var="prod">
    <tr>
      <td>${prod.id}</td>
      <td>${prod.name}</td>
      <td>${prod.price}</td>
    </tr>
  </c:forEach>
  </body>
</html>

需要注意的是jstl標籤裡面訪問的都是pojo的get方法,因為類似於name, price等欄位都是私有的,所以不可能通過obj.propery來訪問到,只能通過getXXX的方式來獲取,這也是為什麼我們需要JavaBean的原因。我們儘量不要在jsp中使用<%= pageContext.request.contextPath %>,這樣的方式來寫java程式碼,而儘可能要使用EL表示式方式

struts2值域valueStack

雖然在上面的演示中,我們通過request,session,application等域物件可以在頁面處理過程中交換資料,但是這類方法更多限定於在jsp頁面中訪問相關資料,對於如果想在action中訪問相關資料,則可以使用struts2框架的值域。action一旦建立,就會生成一個valueStack,他就是一個存放資料的容器。struts2中會將所有的域物件(request,response,application,session)也都存放在了valueStack中,因此最好的方式,咱們都統一稱為valueStack方式來處理資料。

jspf檔案簡化前端資源引用

在web頁面中,有一些css,js等前端資源的引用實際上是公共的,幾乎所有的頁面都需要。並且有的時候我們也需要在所有jsp檔案中都可能需要引用類似工程基地址的變數,這時一個比較好的辦法就是使用類似php的partial.blade.php檔案,將這些東西抽取出來,在所有jsp檔案中通過@include來引用。

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<c:set var="projectbaseuri" value ="${pageContext.request.contextPath}"/>

JVM記憶體分佈

所謂本地方法區是指非Java語言編寫的C或者C++程式需要的棧,而程式計數器則屬於執行緒專有,儲存著每個執行緒執行時場景的堆疊,PC計數器等,可以用於執行緒的切換

資料庫JDBC

jdbc是java定義的一套java訪問資料庫的介面,資料庫的vendor可以根據該介面規範來實現資料庫訪問驅動,比如mysql的驅動為Mysql-JDBC-impl.jar,Oracle-JDBC-impl.jar, SQL servier-JDBC-impl.jar. 

java程式遵循JDBC規範通過對應的驅動來對實際資料庫的訪問。

DAO元件

通過上面的JDBC介面,雖然JAVA程式可以直接訪問到資料庫,但是每次資料庫的訪問還是比較複雜的過程,比如建立連線,構建SQL,執行SQL,關閉sql statement,關閉jdbc連線。為了責任單一,一般需要抽象出來一層DAO元件,上層程式呼叫該元件的方法實現資料庫操作。

資料庫連線池

如上面所描述,每次資料庫的操作都需要建立資料庫的連線,資料庫操作完畢後將連線關閉,而socket建立和關閉是很消耗資源並且緩慢的過程,我們有必要事先建立好這些connection,而資料庫訪問時,臨時從這些connection中取出一個,資料操作完畢後並不真正釋放連線,而是將連線物件返回到連線池,供後續應用使用。

過濾器filter

類似於PHP laravel的middleware,我們可以使用filter來對某些servlet進行保護和授權。

相關推薦

JAVA學習筆記知識積累

 為什麼說Java具有跨平臺特性? 我們知道計算機只認識1,0兩種電平的訊號,所有資訊或者計算指令最終都編碼成16進位制的機器碼,這些機器碼作為程式保存於計算機的記憶體中,由CPU去單個取指令執行直到程式執行完畢。然而計算機能認識的這些機器碼確實不是人類善於處理的,因此人們發明了組合語言,隨後使用匯編器(as

Java學習筆記知識總結

字節數 都是 eem 信息 hashmap 想要 trees 賦值 基本數據 概述 1991年由sun公司開發的名稱為Oak的語言,1994年更名為Java。 JDK:Java Development Kit,Java的開發和運行環境,Java的開發工具和JRE。 JRE:

Java學習筆記之二十二】解析接口在Java繼承中的用法實例分析

ani 復制代碼 ads compute 現在 target body 常量 實現接口 一、定義 Java接口(Interface),是一系列方法的聲明,是一些方法特征的集合,一個接口只有方法的特征沒有方法的實現,因此這些方法可以在不同的地方被不同的類實現,而這些實現可以具

Java學習筆記(二)-------String,StringBuffer,StringBuilder區別以及映射到的同步,異步相關知識

ringbuf 等待 java學習筆記 java學習 單線程 回復 改變 hashtable ble 1. String是不可變類,改變String變量中的值,相當於開辟了新的空間存放新的string變量 2. StringBuffer 可變的類,可以通過append方法改

FPGA軟硬協同設計學習筆記基礎知識(一)

擴展性 產生 新的 pll 多個 分配 每次 調用 span 一、FPGA軟件硬件協同定義: 軟件:一般以軟件語言來描述,類似ARM處理器的嵌入式設計。硬件定義如FPGA,裏面資源有限但可重配置性有許多優點,新的有動態可充配置技術。

Mysql DBA 高級運維學習筆記-索引知識創建索引的多種方法實戰

varchar not 要求 相關 auto 唯一索引 cte lte 推薦 本文是我學習老男孩老師的Mysql DBA 高級運維課程的學習筆記,老男孩老師講的很好,非常感謝老男孩老師。剛剛接觸運維有很多不懂得知識,如果我發表的文章有不正確的地方,請運維行業的精英,老師及時

知識小結】Git 個人學習筆記心得

art over round TP 緩存 PE QQ 的區別 rda https://mp.weixin.qq.com/s/D96dXYfu3XAA4ac456qo0g git架構 工作區:就是你在電腦裏能看到的目錄。 版本庫:工作區有一個隱藏目錄.git,,而是

周報告Java學習筆記

類型 stat 結束 安裝 java學習 tom 固定 有效 過程 (1)本周,安裝tomcat及MySQL等學習軟件,學習Java打代碼的時間大概有十五六個小時吧,解決問題用了大概兩三個小時。(2)下周去練車,去遊泳,學習。(3)漸漸熟練用windows窗口命令創建即完成

5.9 j(java學習筆記)強軟弱虛引用WeakHashMap、IdentityHashMap、EnumMap

一、引用分類 強:執行垃圾回收機制後也不回收,程式出現記憶體溢位也不回收。 軟:在垃圾回收機制執行時判斷記憶體是否已滿,如果記憶體已滿則回收,記憶體充足則不回收。 弱:垃圾回收機制執行後不論記憶體是否充足都會立即回收。 虛:虛引用和沒有引用一樣,必須配合引用佇列使用。   我們來看例子:

Java學習筆記1:計算機基礎知識java語言基礎。

一、計算機基礎知識 1、 計算機是一種能夠按照程式執行,自動、高速處理海量資料的現代化智慧電子裝置。由硬體和軟體所組成,沒有安裝任何軟體的計算機稱為裸機。常見的形式有臺式計算機、筆記本計算機、大型計算機等。 硬體通常由CPU、主機板、記憶體、電源、主機箱、硬碟、顯示卡、鍵盤、滑鼠,顯示器等多

java學習筆記(三)內部類靜態內部類

內部類的建立:    內部類 物件名 = 外部類物件.new 內部類();    外部類是不能使用內部類的成員和 方法    如果外部類和內部類具有相同的成員變數或方法,內部類預設    訪問自己的成員變數 或方法,如果

Java學習筆記——包裝類自動拆裝箱

在Java中資料分為兩種型別:1.基本資料型別   2.引用資料型別 基本資料型別:   int、double、float、long、byte、boolean、char、short 包裝類就是7種基本資料型別對應封裝起來的類,Integer、Double、Flo

Java學習筆記之--------IO流的原理分類

 IO流的原理 在Java程式中,對於資料的輸入/輸出操作以“流”(stream)方式進行。J2SDK提供了各種各樣的“流”類,用以獲取不同種類的資料;程式中通過標準的方法輸入或輸出資料。  IO流的分類 按照流的方向: 輸入流:資料來源到程式。 輸出流:程式到目的

Java學習筆記:前言(Java入門推薦一本Java教材)

Java入門及推薦一本Java教材         開始學習Java了,學習了一陣子,但只是看書、除錯程式,沒有留下什麼記錄。恰好我也有朋友要學Java。索性我就把Java學習過程中,技術方面的經歷寫出來。方便朋友和大家參考。這些文章不會是固定的。即使以前的文章,如果發現有

Java學習筆記05--強制型別轉換 ;內部類(瞭解即可) ;Java異常體系異常處理;iOS中的try catch 塊

===============java相關講解============= 強制型別轉換: 基本資料型別的轉換 小資料型別---->大的資料型別 自動型別轉換 大資料型別----->小資料型別 強制型別轉換

Java學習筆記07--時間日期相關 ;system類;RunTime;MATH類 ;隨機數類;java多執行緒

===============java相關講解============= ∆ 時間及日期相關(Date Calendar SimpleDateFormat) 程式碼示例 public class Demo3 { public

Java學習筆記13-- web伺服器介紹Tomcat的使用;jdk,eclipse,tomcat關係以及安裝順序;http協議

web伺服器介紹及Tomcat的使用 jdk,eclipse,tomcat關係以及安裝順序 1、eclipse安裝前必須要先裝jdk 1、沒有JDK的話,無法安裝或者執行eclipse。 2、JDK 是整個Java的核心,包括了Java執行環境,Java

java學習筆記之webservice(二)--WSDL文件用myeclipse測試webservice

 >>接上篇 一、WSDL 定義:web services description language,用來描述web服務的xml格式的資訊。 標籤的解釋 1. <types>:定義了服務的namespace和關鍵資訊的型別(方法的引數型別和返回值的

java學習筆記-設計模式之單例模式如何防止反射反序列化漏洞

在前一篇文章中,對單例模式列舉了五種實現方式。其中列舉模式擁有出生光環,天生就沒有反射及反序列化漏洞。針對其他四種實現方式,在本篇文章中對懶漢式單例模式實現進行反射及反序列化漏洞測試。 一、通過反射破解懶漢式單例模式 重新建立測試類TestClientNew,通過反射獲取

Java學習筆記29:Java基礎類庫簡介lang包介紹彙總

Java基礎類庫簡介及lang包介紹 https://blog.csdn.net/gaoshoum/article/details/50194181 java.lang包介紹 https://blog.csdn.net/u012834750/article/details/7936155