- Java 面試題1

文章目錄


Java基礎

JVM 運行原理

JVM,是Java虛擬機,Java的代碼都得運行在JVM之上;

Java即是編譯型語言,又是解釋型語言,所以需要有一個編譯器將Java代碼編譯成字節碼文件;

然後Java又是跨平臺的,它能跨平臺是因爲我們在所有要運行Java程序的計算機上裝了JVM,然後將編譯後的class文件交給JVM運行起來;

所以Java在運行的時候會有一個編譯階段,編譯完成之後JVM會把字節碼裝載進來去運行,使用classloader進行裝載;jvm正確讀取到了Java字節碼程序就運行起來了,當程序運行了一段時間之後,就會有一些對象的引用無效了,無效了就得回收,所以JVM裏面就有一個垃圾回收機制GC,它會回收沒有用的對象,防止內存泄漏;


GC 是什麼?爲什麼要有 GC?

GC是垃圾收集的意思,是Java提供的垃圾回收器;
Java提供的GC功能可以監測對象的狀態,它從對象被創建就開始監控這個對象的地址、大小、使用情況,對象沒有被引用時,GC就會收回這些內存空間;

Object類有gc方法,Java虛擬機就是調用這個方法實現垃圾對象的回收的,這個回收是不定時的,也可以顯示調用這個方法,但是這個方法可能不會立即執行,只是通知對象調用這個方法;具體什麼時候執行回收的操作不知道;

優點:引入垃圾回收機制,我們在寫代碼的時候就不用再考慮內存管理了,垃圾回收機制會自動幫我們清理回收沒用的對象,有效的防止內存泄漏;垃圾回收器以單獨的線程運行,在不可預知的情況下對堆內存中已經死亡或者長時間沒有使用的對象進行清理回收;


java 中會存在內存泄漏嗎,請簡單描述

內存泄露就是指一個不再被程序使用的對象或變量一直被佔據在內存中;
Java中有垃圾回收機制可以在對象沒有被引用時清理回收;
Java使用有向圖的方式進行垃圾回收管理,對於互相引用的兩個對象,只要使用它們的進程不可達,GC就會回收它們;

內存泄漏的情況:長生命週期的對象持有短生命週期的對象的引用就可能發生內存泄漏;短生命週期的對象已經不用了,但是還沒其他對象持有引用,所以不能被回收;
比如緩存:加載了一個對象進緩存,然後就不再使用它了,這個對象就會一直被緩存引用;

檢查內存泄漏:把程序的各個分支都執行一遍,然後看那個對象沒有被使用;


類加載器 ClassLoader

類裝載器負責在程序運行時查找class文件,然後加載進JVM;

JVM運行時會產生3個類加載器,根裝載器、擴展類裝載器、應用類裝載器;

根裝載器負責裝載JRE的核心類庫,在JRE目錄下;
擴展類裝載器負責裝載JRE目錄下的擴展目錄ext目錄下的jar包;
應用類裝載器負責裝載類路徑classpath下的類包;

這3個裝載器存在父子層級關係:根裝載器是擴展類裝載器的父裝載器,擴展類裝載器又是應用類裝載器的父裝載器;擴展類裝載器和應用類裝載器是ClassLoader的子類,根裝載器不是;

JVM裝載類時使用”全盤負責委託機制“;全盤負責指的是使用一個裝載器裝載一個類時,除非顯示的指定另一個裝載器,否則這個類所依賴或引用的類也由這個裝載器裝載;委託機制指的是要裝載一個類時,首先委託父裝載器尋找目標類,若父裝載器沒有找到,纔在自己的類路徑下查找類並裝載;因爲這個機制,String類永遠由根裝載器來裝載;


能不能自己寫個類,也叫 java.lang.String?

可以;
但是在使用的時候需要用自己的加載器去加載,否則根加載器永遠只是去加載jre包中的Java.lang.String;


Java 反射

反射機制就是在程序運行時動態獲取類的信息,動態調用對象的屬性和方法;它是以間接的方式操作目標類;Java中有一個Class類,通過這給類的forName方法,傳入類的名稱,就可以獲取到這個類的字節碼文件,然後通過這個字節碼文件,就可以獲取到這個類的所有信息,包括屬性、構造方法、一般方法;通過構造方法可以創建這個類的實例對象,然後就可以調用對象的屬性和方法了;

反射提高了程序的擴展性;

就比如一個已經完成的可以獨立運行的應用程序,想要在後期擴展功能,就要在前期設計的時候,提供一個接口,對外暴露出來,然後在需要擴展功能的時候,就在程序外面創建功能的實現類,實現這個接口;但是外程序裏面不能通過new的方式創建類的對象,因爲程序在設計的時候不知道後期會創建什麼樣的類,所以就要使用反射機制,在程序運行的時候動態獲取類的信息;程序在對外暴露接口的時候,會同時提供一個配置文件,我們在後期創建接口的實現類時,會把類的名稱寫到配置文件裏,程序運行時會讀取這個配置文件,通過配置文件中類的名稱,就能獲取到類的字節碼文件,然後調用類的構造函數創建對象,調用對象的方法;

Tomcat中就使用到了反射機制;Tomcat作爲一個web服務器,它提供的最簡單的功能就是接收請求進行處理並應答,但是怎麼進行處理、怎麼進行應答,Tomcat在設計的時候不知道,因爲對於不同的請求有不同的處理方式,這個要開發者去決定具體的處理和應答的動作;所以Tomcat就對外提供了統一的一個接口servlet,開發者定義子類去實現這個servlet類,然後再子類中定義處理和應答的方式,然後將這個子類的名稱寫到Tomcat提供的web.xml文件中,Tomcat啓動的時候就加載這個配置文件,就會讀取到這個子類的名稱,就能通過反射就能獲取到這個類的信息並進行調用;

1、獲取類的class文件有3種方式:
(1)創建類的對象,然後調用對象的getClass()方法;這個方法是從Object對象繼承過來的;這個方法要先明確具體的類,再創建對象,麻煩;

(2)直接調用類的class屬性;這個屬性是靜態的,不需要創建對象就能獲取到,但是也要明確具體的對象,還是不夠擴展;

(3)通過Class類的forName()方法;傳入指定的類的名稱即可;
Class class = Class.forName("類的名稱");

(4)通過類加載器:
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Class class = loader.loadClass("類的名稱");

2、獲取類的構造函數:
(1)通過clazz對象的newInstance方法創建對象;這個方式相當於使用new調用無參構造函數創建對象;
Object obj = class.newInstance();

(2)先通過clazz對象的getConstructor方法獲取到類的構造函數對象,然後再使用構造函數對象的newInstance方法創建對象;
Constructor c = class.getConstructro(String.class, int.class);
Object obj = c.newInstance("zxj", 25);

3、獲取類的屬性:
Field field = class.getField("name");:獲取類的公有屬性;
Field field = class.getDeclaredField("age");:獲取所有屬性,包括私有的;
field.set(obj,23):給屬性賦值;
field.setAccessible(true);:取消對私有字段的權限檢查,暴力訪問;

4、獲取類的方法:
Method[] methods = clazz.getMethods();:獲取所有公有方法;
Method[] methods = clazz.getDeclaredMethosd();:包括私有方法;
Method method = clazz.getMethod();獲取指定方法;
method.invoke();執行方法;


heap 和 stack 有什麼區別

heap:堆內存;
堆內存中使用new創建的對象,和方法中使用final修飾的變量;堆中存放的對象不會隨方法的結束而消失;
stack:棧內存;
Java程序調用一個方法時,會在棧內存中分配一片連續的空間,用來存儲方法的局部變量,方法執行結束後,彈棧,棧內存中爲這個方法分配的內存空間就會釋放;


&和&&的區別

先說兩者的共同點,再分別說出兩者的特殊之處,並舉例;

  • &和&&都可以用作邏輯與的運算符,表示邏輯與(and),當運算符兩邊的表達式的結果都是true時,整個運算結果才爲true;只要有一個表達式爲false,運算結果就是false;

  • &&有短路功能:若第一個表達式的結果爲false,則不再計算第二個表達式;
    if(str !== null && str.equals("")) 若str爲null,&&後面的表達式不會繼續計算,整個表達式的結果爲false;若str爲null並且使用& ,則後面的表達式會計算,會拋出空指針異常;

  • &可以做位運算符:當&操作符兩邊的表達式不是Boolean類型時,&表示按位與操作;
    通常使用0x0f (0000 1111) 來與一個整數進行& 運算,來獲取整數的低四位;


使用 final 關鍵字修飾一個變量時,是引用不能變,還是引用的對象不能變?

使用final關鍵字修飾變量時,指的是引用變量不能變,引用變量指向的對象中的內容可以改變;

Eg:final StringBuffer sb = new StringBuffer("abcdefg");
sb = new StringBuffer(""); :編譯報錯;
sb.append("sss"):編譯通過;


"=="和 equals 區別

==比較的是基本數據類型、或引用變量在內存中存儲的數值是否相等;

如果一個變量指向的是對象類型的數據,那這時候設計到了兩塊內存:一個是存儲變量的棧內存,一個是存儲對象的堆內存,而變量在棧內存中存儲的數值是對象在堆內存中佔用的首地址的值;這時候使用==比較的是兩個變量指向的兩個對象在內存中的地址是否相同,也就是看這兩個變量是否指向同一個對象;

equals比較的是兩個獨立的對象的內容是否相同;

就比如通過new的方式創建兩個String類型的對象,值都是"xy",然後創建兩個變量a和b分別指向其中的一個對象,因爲這是創建了2個對象,所以它們在堆內存中的地址不同,就說明變量ab存儲的值不同,這時候如果比較a==b,結果會返回false,如果比較a.equals(b),結果返回true,因爲equals比較的是對象的內容,兩個對象存儲的都是xy

但是如果一個類沒有定義自己的equals方法,那麼它會使用從Object繼承過來的equals方法,而這個方法內部使用的是==``,也就是比較的是對象在內存中的首地址的值,這和直接使用==`進行比較是一樣的;所以,如果要比較兩個對象的內容是否相等,就要重寫這個方法;


Assert

Assert:斷言,調試代碼用的,就是程序中的一條語句,對一個布爾表達式進行判斷,正在運行的程序必須保證這個布爾表達式的結果爲true,如果爲false,Assert就會發出警告或退出程序;

一般在開發和測試時開啓,程序發佈後關閉斷言;


面向對象

什麼是面向對象 - 三大特徵

面向對象有三大特徵:封裝、繼承、多態;

封裝:將對象的屬性和行爲抽象出來包裝到類中,隱藏對象的屬性和實現細節,然後對外提供公共的訪問方式;

繼承:就是在定義和實現一個類的時候,可以在一個已經存在的類的基礎之上進行,把這個已經存在的類的內容做爲自己的內容,然後添加新的東西,或者改進原來的方法,這就叫繼承,繼承可以使子類自動擁有父類的變量和方法,提高了代碼的重用性;同時讓類與類之間產生了關係,爲多態提供了前提;

多態:指的是在程序中定義的一個引用變量所指向的具體類型,和通過這個變量調用的方法,在程序定義的時候不能確定,而是要在程序運行時才能知道這個引用變量指向的是哪個類的實例對象,通過這個引用變量調用的方法是哪個對象的方法;

因爲只有在程序運行時才能確定具體的類,這樣就可以在不修改源代碼,而是在程序運行時,給引用變量指定不同的類實例對象,使這個引用變量可以有多個運行狀態;這就是多態;

但是多態的前提是要有繼承或實現關係,Java中實現多態的機制就是父類或接口的引用指向子類或實現類的對象,也就是說在程序定義的時候定義一個父類型的引用變量,然後調用父類中的方法,在程序運行的時候給這個引用變量指定一個子類的實例對象,子類對象的方法會覆蓋父類的方法;這個引用變量只能調用父類中有的方法,不能調用子類特有的方法;


靜態代碼塊、構造代碼塊、局部代碼塊

1、靜態代碼塊
(1)java類中使用static關鍵字聲明的代碼塊:static{ … },不能在方法體內定義;
(2)在JVM加載類時,隨着類的加載而加載並執行,所以靜態代碼塊先於主方法執行,而且只執行一次(因爲只加載一次);
(3)用於給類的屬性進行初始化;

2、構造代碼塊
(1)java類中定義的,沒有使用static關鍵字進行聲明的代碼塊;
(2)構造代碼塊會在構造函數被調用時執行,且優先於this()語句執行;(java編譯器在編譯時會先將構造代碼塊中的代碼移到構造函數中執行,構造函數中原有的代碼最後執行)
(3)用於給對象進行初始化;

3、局部代碼塊
(1)在方法中定義的代碼塊;(不能使用static關鍵字進行聲明)
(2)作用:在方法中,如果要縮短變量的壽命,可以使用;
方法中,某段代碼之後,都不再使用某個變量(這個變量有可能是一個很大的Map集合,很佔內存),則可以將其定義到局部代碼塊中,及時結束其生命週期,釋放空間;


靜態變量和實例變量的區別

1、定義靜態變量要在變量前加上static關鍵字,實例變量不用加;

2、實例變量是屬於某個對象的屬性,必須創建實例對象,纔會在堆內存中分配空間,才能使用這個變量;

靜態變量不屬於某個對象,而是屬於類的,所以也叫類變量,它是隨着類的加載而加載進內存的,會在方法區的靜態區分配空間,不需要創建對象就可以使用;

實例變量只能通過對象調用,靜態變量既能通過對象調用,也能通過類直接調用;

3、靜態變量存儲的是共享的數據,實例變量的存儲的是特有的數據;


是否可以從一個 static 方法內部發出對非 static 方法的調用

不可以,因爲非static方法是要跟對象關聯在一起的,必須創建也會對象之後,才能通過對象進行調用,而static的方法不需要創建對象就能直接調用;如果是通過類名直接調用static方法的,而沒有創建對象,那這時候從static方法裏面發出對非static方法的調用,這個方法沒有對象與之關聯,邏輯無法成立;


Overload 和 Override 的區別,Overloaded 的方法是否可以改變返回值的類型?

Overload是重載,Override是重寫、覆蓋;

重載指得是在同一個類中可以存在多個名稱相同的方法,只要它們的參數類型或參數個數不同即可;在通過方法名調用這些重載的方法時,JVM會根據參數列表來判斷具體調用的是哪個方法;
使用重載時,只能通過參數個數、參數類型來實現,若同一個方法中的幾個參數的類型不同時,也可以通過參數的順序來進行重載;但是不能通過方法的訪問權限、返回值類型、拋出的異常來進行重載,異常的類型和個數不會對重載造成影響;而對於返回值類型,如果參數列表不同,返回值也可以不同,但是如果兩個方法的參數列表完全一樣,想要通過不同的返回值類型來實現方法的重載,這是不可以的,因爲如果我們調用方法不關注返回值結果,不定義變量來接收返回值,那麼就會在調用方法時出現調用的不確定性,不知道該調用哪個方法;

重寫指的是在子類繼承父類的時候,若子類中有方法和父類中的方法名稱、參數列表完全一樣,那麼子類的方法就會覆蓋父類的方法去運行,在通過子類創建對象調用方法時,調用的是子類中的這個方法,這也是多態的一種體現;子類通過覆蓋父類的方法去擴展父類的功能;
子類覆蓋父類的方法時,只能拋出比父類更少的異常,或者是子類拋出的異常是父類拋出的異常的子類,因爲子類可以解決父類的一些問題,不能比父類有更多的問題;子類方法的權限要大於等於父類的方法的權限,不能比父類的權限更小;如果父類中的方法的權限是private,那子類中的方法就相當於是新加的一個新的方法,不是對父類方法的覆蓋,也就不用管覆蓋的限制了。覆蓋方法的返回值要一樣;

靜態只能覆蓋靜態,或被靜態覆蓋;
構造函數不能被覆蓋,因爲構造函數不能被繼承,但是它可以被重載;一個類中可以有多個構造函數同時存在,它們就是以重載的形式存在的;


抽象類和接口的區別

抽象類指的是使用abstract關鍵字定義的類,抽象類裏面既有抽象方法,又有普通方法,含有抽象方法的類必須要定義成抽象類;抽象類不能直接創建對象,需要定義子類繼承抽象類,然後實現裏面的所有抽象方法才能創建對象;
抽象類與普通類的唯一區別:就是不能創建實例對象和允許有 abstract 方法;

接口是抽象類的一個特例,當抽象類裏面的所有方法都是抽象方法時,這個類就是一個接口,接口使用interface關鍵字定義;接口中的方法定義默認爲public abstract類型,裏面的成員變量默認類型是public static final;

1、抽象類中可以有構造方法,接口中不能有構造方法;
2、抽象類中可以有普通成員變量,接口中不能有普通成員變量,只能有public static final變量;
3、抽象類中可以有普通方法,接口中不能有;
4、抽象類中可以有靜態方法,接口中不能有靜態方法;
5、抽象類和接口中都可以有靜態變量;
6、一個類可以實現多個接口,但只能繼承一個抽象類;


什麼是內部類、Static Nested Class

內部類,就是在一個類的內部定義的類;

1、內部類不能是靜態的;
2、內部類可以直接訪問外部類的成員,因爲內部類持有外部類的this引用,外部類要訪問內部類的成員就要先創建內部類的對象,然後通過對象調用;
3、內部類可以定義在外部類的方法裏,也可以定義在外部類的方法外面;
如果定義在方法外面,那內部類就和外部類的成員變量一樣有4中訪問類型,可以決定內部類對其他類是否可見;
如果定義在方法裏面,那麼內部類不能有訪問類型修飾符,就和方法的局部變量一樣;
只能訪問方法裏面被定義成final的變量;
4、創建內部類對象時,要先創建外部類的對象,然後通過外部類對象去創建內部類的對象;

什麼時候使用內部類:如果在描述一個事物a的時候,發現事物描述中還有事物B,而且事物B還在訪問事物A的內容,這時候就將事物B描述成內部類;

靜態嵌套類:如果內部類中有靜態成員,那麼這個內部類也要定義成static類型的,static定義的內部類就是內部嵌套類;內部嵌套類不再具有內部類的特性,可以說內部嵌套類不是內部類;
1、在外面引用靜態嵌套類不需要創建靜態嵌套類對象,直接創建外部類對象然後調用靜態嵌套類的類名即可;
2、靜態嵌套類可以直接訪問外部類的靜態成員,不需要加上外部類的類名,但是不能直接訪問外部類的非靜態成員,需要通過外部類對象訪問;

匿名內部類必須實現一個接口或繼承一個類;


是否可以繼承 String 類?

不可以;
java.lang.String 類是final 類型的,因此不可以繼承這個類、不能修改這個類;


String 、 StringBuffer 、StringBuilder的區別

這3個類都可以用來存儲、操作字符串;

String類定義成final類型的,使用它創建的字符串不可以被修改,使用StringBuffer存儲的字符串可以被修改;

String重寫了equals方法,StringBuffer沒有重寫equals方法;
String(“abc”).equals(newString(“abc”) = true;
new StringBuffer(“abc”).equals(newStringBuffer(“abc”);

StringBuffer是線程同步的,常用於多線程;StringBuilder是線程不同步的,常用於單線程,效率高;


final, finally, finalize 的區別

final是用來聲明屬性、方法、和類的,final聲明的屬性的值不可變,方法不能被覆蓋,類不能被繼承;

finally是異常處理語句結構的一部分,表示不管有沒有異常,finally中的語句都會被執行;

finalize是Object的一個方法,在垃圾回收器執行的時候會調用要被回收的對象的這個方法;可以重寫這個方法,在垃圾收集時進行其他操作,比如關閉資源文件等;


Java 中的異常處理機制的簡單原理和應用

異常指的是程序在運行時所發生的非正常的情況或錯誤;

Java使用面向對象的方式來處理異常,就是把程序中發生的每個異常都分別封裝成一個對象來表示,對象中包含異常的信息;然後Java對異常進行了分類,不同的異常分別用不同的類表示,所有的異常都有一個根類Throwable,Throwable類下面又有2個子類:Exception、Error;

Error表示程序本身無法處理的一些嚴重的問題,需要直接修改程序,比如:內存溢出、線程死鎖;

Exception表示程序可以處理的問題,又分爲運行時異常和編譯時檢測異常;

運行時異常指的是RuntimeException體系,是程序本身的缺陷導致的問題,一般是因爲調用者錯誤調用方法導致的,比如空指針異常、數組角標越界異常、類轉換異常;

編譯時檢測異常指的是Exception子類中除了運行時異常體系以外的其他異常,出現這種異常需要在編譯時就進行檢測,然後進行處理;

編譯型檢測異常需要使用try/catch進行捕捉處理,或者在方法聲明處throws拋出,運行時異常不需要;


throws 和 throw區別

throws使用在方法聲明處,拋出的是異常類,可以拋出多個,使用逗號隔開;
throw用在方法裏面,拋出的是異常對象;
編譯型檢測異常需要使用try/catch進行捕捉處理,或者在方法聲明處throws拋出,運行時異常不需要;
自定義異常繼承Exception時需要在方法聲明處throws拋出;


JAVA 如何進行異常處理

(1)在方法聲明處使用throws捕獲異常並向外拋出,
(2)或者在方法內部使用try/catch捕獲異常並進行處理;
(3)也可以捕獲到異常不進行處理,使用throw拋出;也要在方法聲明處throws再拋出一次;
finally 是無論是否有異常都會被處理的語句;

一段代碼如果會拋出多個異常,就要使用多個catch分別進行鍼對性的處理;

子類覆蓋父類方法時,
若父類的方法拋出了異常,那麼子類方法只能拋出父類異常,或者父類異常的子類;
若父類拋出多個異常,子類只能拋出父類異常的子集;
若父類沒有拋出異常,子類不能拋出任何異常,若有異常,只能try/catch捕捉處理;

單例模式關鍵字 - volatile

class Singleton{
    private volatile static Singleton singleton;
    private Singleton(){}       
    public static Singleton getInstance(){       
        if(singleton == null){                   // 1
            synchronized(Singleton.class){       // 2
                if(singleton == null){           // 3
                    singleton = new Singleton(); // 4
                }
            }
        } 
        return singleton;           
    }
}

4處new Single()的操作是分成幾個指令完成的:
(1)分配內存空間;
(2)初始化對象;
(3)返回內存地址給引用變量;
CPU爲了優化程序的執行,可能會進行處理器優化和編譯器指令重拍;

指令重排可能將2、3兩步的執行順序變換,先執行3,返回內存地址給變量引用,然後再初始化對象;這就會導致對象還沒有初始化,就被引用變量使用了;

若有線程A、B同時執行到2,當線程A執行到4的new Single()操作時,由於指令重排,導致先將對象地址賦值給變量引用,還沒開的及初始化,CPU的執行權就切換到了線程B,這時候,線程B判斷變量s已經不爲null,就不再進行new的操作,就直接使用了還沒有被初始化的引用變量了;

volatile禁止編輯器進行指令重排;


多線程

什麼是多線程

進程就是正在進行中的程序;
當一個程序開始啓動的時候,它就會在內存中開闢一段空間,這個空間就是進程;進程其實對應的是應用程序在內存中所屬的空間;進程是不直接執行的,它只是在分配應用程序的內存空間;

線程是進程中的一個負責程序執行的執行路徑;
一個進程中至少有一個線程,就是main方法執行的那個線程,稱爲主線程;
一個進程中可以有多個執行路徑同時運行,稱爲多線程;
每個線程都有自己運行的代碼內容,這個內容稱爲線程要執行的任務;
開啓線程是爲了同時運行多部分代碼;

Java的JVM只能開1000個線程,超過就會出現內存溢出的問題;


線程的基本狀態以及狀態之間的關係

在這裏插入圖片描述
運行、阻塞、凍結、結束;
線程調用start方法時,就進入運行狀態,運行狀態的線程具備CPU的執行資格和執行權,當對一個正在運行的線程執行wait或sleep方法時,線程就會進入凍結狀態,釋放CPU的執行權和執行資格;當sleep設置的時間到,或使用notify喚醒線程後,線程就會進入阻塞狀態,具備CPU執行資格,但是不具備執行權,正在等待執行權;當線程運行完自己的任務之後,就變成結束狀態;


創建線程的方法

(1)創建一個類繼承Thread類,重寫run方法,然後直接創建子類的對象,調用start方法開啓線程並執行線程對象的run方法;
(2)創建一個類實現Runnable接口,然後實現run方法,然後使用Thread類創建線程對象,並將Runnable接口實現類的對象作爲Thread類的構造函數的參數進行傳遞;最後調用start方法開啓線程運行線程的任務;

使用Runnable接口的好處:避免了Java單繼承的侷限性;


wait和sleep方法的區別

sleep是Thread類的方法,wait是Object類的方法;這兩個方法都能讓線程進入凍結狀態;
sleep必須指定時間,wait可以指定時間也可以不指定時間;

sleep就是正在執行的線程主動讓出CPU,讓CPU去執行其他線程,在sleep指定的時間結束後,CPU會回到這個線程繼續執行操作;但是sleep不會釋放鎖,也就是說如果當前線程進入到了同步鎖,那它sleep後,其他線程獲取到CPU的執行權也不能操作同步鎖中的代碼;

wait釋放執行權也釋放鎖,其他線程獲得CPU執行權後,如果也獲得了同步鎖,就可以操作同步中的代碼了;使用wait進入凍結狀態的線程不會自己醒,需要其他線程調用notify方法才能喚醒,notify是隨機喚醒線程池中的一個凍結狀態的線程,被喚醒的線程不會立刻執行,因爲執行notify方法的線程並不釋放CPU的執行權和鎖,而是繼續執行自己的線程任務,它只是通知被喚醒的線程可以參與鎖競爭了,被喚醒的線程獲取到鎖纔會繼續執行自己的任務;

public class test extends Date {
	public static void main(String[] args) {
		new Thread(new Thread1()).start();
		try {
			Thread.sleep(10);
		} catch (InterruptedException e) {
		}
		new Thread(new Thread2()).start();
	}
}
class Thread1 implements Runnable {
	public void run() {
		synchronized (test.class) {
			System.out.println("enter thread1...");
			System.out.println("thread1 is waiting");
			try {
				test.class.wait();
			} catch (InterruptedException e) {
			}
			System.out.println("thread1 is going on...");
			System.out.println("thread1 is being over!");
		}
	}
}
class Thread2 implements Runnable {
	public void run() {
		synchronized (test.class) {
			System.out.println("enter thread2...");
			System.out.println("thread2 notify other thread can release wait status..");
			test.class.notify();
			System.out.println("thread2 is sleeping ten millisecond...");
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
			}
			System.out.println("thread2is going on...");
			System.out.println("thread2is being over!");
		}
	}
}
enter thread1...
thread1 is waiting
enter thread2...
thread2 notify other thread can release wait status..
thread2 is sleeping ten millisecond...
thread2is going on...
thread2is being over!
thread1 is going on...
thread1 is being over!

同步和異步有何異同,在什麼情況下分別使用他們?舉例說明

同步就是發送一個請求,需要等待請求的返回結果,然後才能發送下一個請求;
異步是發送一個請求,不需要等到回覆就可以發下一個請求;

操作共享數據必須使用同步;共享數據就是正在寫的數據可能會被另一個線程讀到,或者正在讀的數據已經被另一個線程寫過了;

如果通過對象調用一個方法,這個方法的執行需要很長時間,並且不需要等待方法的返回值,這個時候就應該使用異步,異步執行的效率高;


死鎖代碼

死鎖:就是兩個線程都持有一個鎖,然後都在等待對方持有的鎖,不獲取到對方持有的鎖,就不會釋放自己持有的鎖;

class Deadlock implements Runnable {
	private boolean flag;
	public Deadlock(boolean flag) {this.flag = flag;}
	public void run() {
		if (flag) {
			synchronized (MyLock.locka) {
				System.out.println(Thread.currentThread().getName() + "...if...locka");
				synchronized (MyLock.lockb) {
					System.out.println(Thread.currentThread().getName() + "...if...lockb");
				}
			}
		} else {
			synchronized (MyLock.lockb) {
				System.out.println(Thread.currentThread().getName() + "...else...lockb");
				synchronized (MyLock.locka) {
					System.out.println(Thread.currentThread().getName() + "...else...locka");
				}
			}
		}
	}
}
class MyLock {
	public static final Object locka = new Object();
	public static final Object lockb = new Object();
}
public class Test {
	public static void main(String[] args) {
		Deadlock d1 = new Deadlock(true);
		Deadlock d2 = new Deadlock(false);
		Thread t1 = new Thread(d1);
		Thread t2 = new Thread(d2);
		t1.start();
		t2.start();
	}
}

synchronized 和 java.util.concurrent.locks.Lock 的異同

使用synchronized能完成的功能,使用Lock都能完成;

1、同步函數/同步代碼塊 只能有一個鎖,一個鎖只能對應一組監聽器的方法:wait、notify、notifyAll;

Lock是將同步函數/同步代碼塊和鎖放在一起封裝成一個對象,然後將監聽器的方法封裝成一個對象Condition接口,使用Lock創建的鎖,一個鎖上可以掛多個Condition對象,也就是說一個Lock鎖上面可以掛多組監聽器的方法;

2、同步代碼塊對鎖的操作是隱式的,就是說使用同步代碼塊開啓鎖和釋放鎖的操作我們看不見;使用Lock作爲鎖,我們可以自己顯示的開啓鎖、釋放鎖;

3、使用同步代碼塊的notify方法時,喚醒的線程是隨機的,使用Lock+Condition實現同步的時候,可以指定喚醒哪個線程;比如一個鎖上指定了兩組監視器方法,某個線程使用監視器a的await方法凍結的,喚醒時可以調用a.singal方法只喚醒a監視器凍結的線程,而不會喚醒由b監視器凍結的線程;


集合框架

介紹 Collection 框架的結構

在這裏插入圖片描述

Collection

  • List有序,可重複
    • Vector:內部使用數組結構,線程同步,增刪、查詢速度都慢;
    • ArrayList:內部使用數組結構,線程不同步,查詢速度快;
    • LinkedList:內部使用鏈表結構,線程不同步,增刪速度快;
  • Set無序,不可重複
    • HashSet:內部結構使用hash表存儲數據,不同步;
      • LinkedHashSet有序不重複
    • TreeSet

Map:鍵唯一,值可重複;

  • HashTable:內部結構是哈希表,同步,鍵值都不允許爲null;
  • Properties:用來存儲鍵值對類型的配置文件信息,只能存儲String類型的數據,可以和IO流技術相結合,數據可以保存到流中,也能從流中獲取;
  • HashMap:內部結構是哈希表,鍵值允許爲null;
    • LinkedHashMap
  • TreeMap:內部結構是二叉樹,可以對鍵排序;

①看到array:就要想到數組,就要想到查詢快,有角標;	
②看到link:就要想到鏈表,就要想到增刪快,就要想要 add get remove+frist last方法;
③看到hash:就要想到哈希表,就要想到唯一性、元素需覆蓋hashcode和equals方法;
④看到tree:就要想到二叉樹,就要想要排序、兩個接口Comparable、Comparator ; 

ArrayList:判斷集合中是否包含某對象時,使用contains方法,這個方法內部調用equals方法實現的,所以存入ArrayList中的對象的類要重寫equals方法

HashSet:哈希表判斷兩個元素是否相同,首先判斷兩個元素的哈希值是否相同,若相同,再判斷兩個對象的內容是否相同;所以如果要將對象存儲到HashSet中,定義對象類時,需要重寫類的hashcode方法和equals方法
HashSet按照hashcode的值做某種運算計算出存儲位置,然後進行存儲,不是按照hashcode的值的大小進行排序存儲的;

LinkedHashSet按插入的順序存儲,被存儲對象的hashcode方法不是從來計算存儲位置的,而是用來比較兩個對象是否相等;

TreeSet:可以對Set集合中的元素進行指定順序的排序;
1、判斷元素唯一性方式:compareTo;不看hashcode+equals;
2、對元素進行排序的方式:兩種方式都實現了以比較器爲主;
(1)讓元素自身具備比較的功能:定義類,實現Comparable接口,重寫compareTo方法;
(2)讓集合自身具備比較的功能:定義一個比較器類,實現Comparator接口,重寫compare方法;然後創建比較器的實例,作爲參數在創建TreeSet集合時傳遞;

取出map集合中的元素:
(1)先通過keySet方法取出所有key,然後遍歷key,通過get(key)取出值;
(2)先將鍵值對的映射關係作爲對象存儲到Set集合中,然後遍歷Set集合,獲取鍵值對,然後再分別獲取鍵和值;存入Set的類型是Map.Entry;getKey();getValue();

HashMap:鍵唯一,相同則覆蓋;
若鍵存儲的是對象類型,則需要重寫hashcode+equals方法;

TreeMap:若鍵存儲的是對象類型,則需要定義比較器;

有序 指的是存入和取出數據的順序一致;按照從大到小或從小到大的順序指的是排序;


ArrayList 和 Vector 的區別

1、這兩個類都實現了List接口,都是有序集合,內部使用數組結構,所以可以使用索引號獲取指定位置的元素,並且存儲的數據可重複;

2、Vector是線程安全的,它的方法之間是線程同步的,ArrayList是線程不安全的,它的方法之間線程不同步;如果使用單線程操作集合最好使用ArrayList,因爲它不考慮線程安全問題操作的效率會高;如果是多線程操作集合,最好使用Vector;

3、這兩個類都有一個初始的容量大小,當存儲的數據超過初始容量時,它們都會自動增加空間,Vector默認增長爲原來的兩倍,ArrayList默認增長爲原來的1.5倍;Vector可以設置每次增長的大小,ArrayList不能設置;


HashMap 和 Hashtable 的區別

1、HashTable繼承Dictionary類,HashMap實現Map接口;

2、HashTable是線程安全的,多線程操作集合時,最好使用它;HashMap是線程不安全的,單線程操作集合時使用它效率高,多線程操作集合時,使用HashMap要自己實現同步;

3、HashTable不允許鍵和值存儲null值,HashMap可以;


List 和 Map 區別

1、List繼承Collection接口,Map不是繼承Collection接口的;

2、List存儲的是單列數據,Map是以鍵值對的形式存儲是雙列數據;

3、List中存儲的數據有序,可重複;Map中存儲的數據無序,鍵不可重複,值可重複;


List、Map、Set 三個接口,存取元素時,各有什麼特點

1、List和Set都是存儲單列元素的集合,都繼承Collection接口,Map是存儲雙列元素的集合,不是繼承Collection接口的;

2、List存儲的元素有序,每次調用add方法添加元素時,元素都會按順序存儲,也可以通過索引指定元素要存儲的位置;
List中的元素可以重複,一個對象可以被多次存儲進LIst中,每次調用add方法,這個對象就被插入進集合中一次,但是不是把這個對象本身存儲進去,而是存儲的是這個對象在內存中的地址,也就是在集合中存進一個索引變量指向這個對象,這個對象被多次添加時,就相當於集合中有多個索引變量指向這個對象;
獲取元素時,可以使用get方法指定索引獲取指定位置的元素,也可以使用迭代器遍歷集合元素;

3、Set集合存儲的元素是無序不重複的,不重複指的是兩個元素不能equals相等,如果集合中存儲了一個對象A,再使用add方法添加對象B,若對象A與對象equals相等,那元素B就不能存入集合中,add方法會返回布爾型的值false,若集合中沒有某個元素,使用add方法可以成功插入這個元素,方法返回true;
獲取元素時,只能使用迭代器遍歷集合,不能通過指定位置來獲取指定元素,因爲Set集合中的元素沒有索引號;

4、Map集合使用put方法添加元素,每次存儲元素都要存儲一對key/value,鍵不能重複,判斷鍵重複也是使用的equals方法,所以如果key存儲的是對象類型的數據,對象類需要重寫equal方法;value可以重複;
獲取元素值時,可以使用get方法指定key,獲取key對應的value,可以獲取所有的key值,可以獲取所有的value值,還可以獲取key/value組成的Map.Entry類型的對象的集合,然後使用迭代器遍歷;


Collection 和 Collections 的區別

Collection是集合類的父接口,繼承它的接口主要有List、Set;
Collections是針對集合類提供的一個幫助類,它提供了一系列靜態方法實現對集合類的搜索、排序等操作,不用創建對象,可以直接拿來使用;


IO 流

IO流體系

字節流:

  • InputStream
    • FileInputStream
    • BufferedInputStream
  • OutputStream
    • FileOutputStream
    • BufferedOutputStream

字符流:

  • Reader
    • InputStreamReader
      • FileReader
    • BufferedReader
  • Writer
    • OutputStreamWriter
      • FIleWriter
    • BufferedWriter

字節流與字符流的區別

字節流是針對字節數據進行操作的,如果操作的是純文本的文件,將純文本的數據寫入到底層設備,就要要先字符數據轉換爲字節數據;將底層設備的數據寫入到純文本中,需要將字節數據轉換爲字符數據;
因爲操作純文本的需求比較多,就提供了一個字符流,它是對字節流的包裝類,可以直接操作字符,不用再轉換成字節,轉換操作字符流內部完成;


什麼是 java 序列化,如何實現 java 序列化,解釋 Serializable 接口的作用

序列化,就是把Java對象轉換成字節流,然後持久化到硬盤上,或在網絡上傳輸;
反序列化,就是根據對象的class文件將序列化文件恢復成Java對象;

我們可以自己寫代碼把一個Java對象轉換成某種格式的字節流然後再進行傳輸,也可以使用JRE提供的字節流方法writeObject實現,要想使用Java提供的方法,被傳輸的對象的類就要實現Serializable接口,告訴Java編譯器,這個類創建的對象可以序列化;可以被序列化的類才能被OutputStream.writeObject()方法操作;

Serializable接口是一個標記接口,沒有需要實現的方法,它只是爲了標註實現它的類創建的對象可以被序列化;它會給類提供一個序列號,使用該類創建對象時,這個序列號也會添加到對象中,反序列化時,會判斷序列化對象和生成序列化對象的類是否是同一個版本;不是同一版本,反序列化會失敗;類每次修改,序列號就會改變;

序列化時,靜態變量不會被序列化,如果有非靜態變量也不想被序列化,可以使用transient關鍵字修飾;


算法

排序都有哪幾種方法?請列舉;用 JAVA 實現一個快速排序

冒泡排序:每次固定最大值;
選擇排序:每次固定最小值;
快速排序:每次固定中間值;左右兩側分別遞歸;


Java 框架

Spring IoC

IOC:控制反轉;指的是將對象的創建工作交給Spring容器進行;
實現技術:XML配置文件 + Dom4j + 工廠設計模式 + 反射;
(1)創建XML配置文件,配置需要創建對象的類:
(2)創建工廠類,使用dom4j解析xml配置文件,使用反射創建對象實例:
public class Factory{
public static User getUser(){
// 使用dom4j解析xml配置文件,根據ID獲取class的值;
String className = “class屬性值”;
Class class = Class.forName(className);
User user = class.newInstance();
return user;
}
}
(3)在使用對象的類中直接調用工廠類的靜態方法獲取對象實例即可;
因爲工廠類是通過反射創建對象的,不是new出來的,所以耦合度降低了;


Spring AOP的原理

AOP:面向切面編程;指的是擴展功能不修改源代碼,而是通過配置實現;
採用橫向抽取機制,底層使用動態代理技術,在程序運行期動態織入增強的代碼;
擴展功能:
(1)原始方法是直接在源代碼中進行添加;
(2)改進:在子類中添加功能,缺陷:若在父類中修改方法名,子類中也要修改;
(3)AOP採用橫向抽取機制;
AOP動態代理:
(1)有接口的使用JDK的動態代理技術:
JDK動態代理通過反射來接收被代理的類,然後創建代理對象,
該代理對象和實現類對象平級,兩個對象具有相同的功能;
要求被代理的類必須要實現某個接口;
(2)無接口的使用cjlib的動態代理技術:
創建類的子類的代理對象,在子類中調用父類的方法完成增強;
這種方法要求類不能被標爲final(final的類不能被繼承);
AOP橫向抽取的是類的方法的共性內容;
繼承的縱向抽取的是類的共性方法;


Java web

HTTP 請求的 GET 與 POST 方式的區別

1、get提交,提交的信息都封裝到了請求消息的請求行中,會顯示在地址欄中,對敏感的數據信息不安全,提交的信息數量有限,只能提交純文本;
post提交,將提交的信息封裝到了請求體中,不會在地址欄中顯示,對敏感數據信息安全,可以提交大體積的數據,可以提交純文本和二進制文件;

2、get一般用於從服務器上獲取數據,post一般用於向服務器傳送數據;

3、如果提交的信息中有中文,服務器默認會使用iso8859解碼,會出現亂碼,可以先使用iso8859對提交的消息編碼,然後再指定中文碼錶進行解碼;這種解決方法對get、post都有效,post還有一種解決方法,就是使用服務器端的request對象,調用它的setCharactorEncoding 方法,直接指定中文碼錶解碼;

post:在web.xml中添加亂碼過濾器,指定初始參數的encoding爲utf-8;

4、在和服務器交互時,使用url方式使用get提交,使用超鏈接方式用get提交,使用表單方式get、post都可以,推薦使用post;

5、get可以被瀏覽器緩存,post不可以;


什麼是 servlet

Servlet是在服務器上運行的Java小程序,主要用來接收並處理客戶端發送過來的請求,然後返回響應;
Servlet是一個Java類,創建一個類實現Servlet接口,然後將類部署到服務器,這個類就可以作爲Servlet處理http請求;


說一說 Servlet 的生命週期

1、Servlet容器啓動或客戶端發送請求時,Servlet容器就會查找內存中是否存在這個Servlet實例,若存在,直接拿來處理客戶端請求,若不存在,就創建一個實例;

2、實例化後,Servlet容器就調用它的init方法進行初始化;init方法只執行一次;

3、初始化後,這個Servlet就處於就緒狀態,等待客戶端請求,當接收到客戶端請求時,就調用service方法處理請求,如果是HttpServlet,service會根據客戶端的請求的類型去調用doGet/doPost方法處理請求;
(HttpServlet是針對Http協議的Servlet子類)

4、Servlet容器關閉時,容器會調用Servlet的destory方法銷燬Servlet實例,釋放內存;


Servlet 的基本架構

public class myServlet extends HttpServlet {
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		// TODO Auto-generated method stub
		super.doGet(req, resp);
	}
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		// TODO Auto-generated method stub
		super.doPost(req, resp);
	}
}

JSP與Servlet有什麼區別

JSP本質上是一個Servlet,第一次訪問JSP頁面的時候,服務器會將JSP轉換成Servlet文件,然後再編譯成class文件;

JSP是在HTML中嵌入Java代碼,主要用於頁面顯示,Servlet是在Java代碼中嵌入HTML代碼,主要用來實現邏輯控制;


servlet API 中 forward()與 redirect()的區別

轉發forward是帶着轉發前的請求的參數的,重定向redirect是新的請求;

1、實際發生位置不同,地址欄不同:

  • 轉發是由服務器進行跳轉的,瀏覽器地址不變,也就是說轉發是對瀏覽器透明的,瀏覽器不知道該跳轉的動作;
    轉發是一次http請求,一次轉發中request和response對象是同一個,所以可以使用request對象進行servlet之間的通信;
  • 重定向是由瀏覽器進行跳轉的,是兩個Http請求,瀏覽器地址欄會變,兩個請求中的request對象不是同一個,不能進行servlet之間的通信;

2、資源地址寫法不同:

  • 轉發:"/" 代表本應用程序的根目錄,轉發 資源是給服務器用的,直接從資源名開始寫;
  • 重定向:"/" 代表webapps目錄,重定向 資源的是給瀏覽器用的,要從應用名開始;

3、能去往的URL範圍不同:

  • 轉發:是服務器跳轉,只能去往當前web應用的資源;
  • 重定向:是瀏覽器跳轉,可以去任何資源;

4、傳遞數據類型不同:

  • 轉發:request域對象可以傳遞各種類型的數據,包括對象;
  • 重定向:只能傳遞字符串;

5、跳轉的時間不同:

  • 轉發:執行到跳轉語句時立即跳轉;
  • 重定向:整個頁面執行完後才進行跳轉;

6、典型的應用場景:

  • 轉發: 訪問 Servlet 處理業務邏輯,然後 forward 到 jsp 顯示處理結果,瀏覽器裏 URL 不變;
  • 重定向: 提交表單,處理成功後 redirect 到另一個 jsp,防止表單重複提交,瀏覽器裏 URL 變了;

什麼情況下調用 doGet()和 doPost()

直接在地址欄輸入URL、或者使用超鏈接、或者使用表單並且表單的方法是get時,使用doGet方法;

使用表單提交信息並且方法是Post時調用doPost方法;


request.getAttribute()和 request.getParameter()有何區別

getParameter用來接收使用url、或者超鏈接、或者表單提交傳遞的參數,不用設置參數,所以沒有setParameter方法,只能接收String類型的參數;

getAttribute表示用來從request域獲取通過setAttribute方法設置的屬性值,數據類型可以是對象類型;


JSP 腳本標識

1、jsp 表達式:用於向jsp頁面輸出信息;
<%= 表達式%>

2、聲明標識:在jsp頁面中定義全局變量或方法,可以在整個頁面使用;
服務器執行頁面時會將jsp頁面轉換成servlet,在該類中,會把jsp聲明標識定義的變量和方法轉換爲類的成員變量和方法;
<%! 聲明變量或方法的代碼;%>

3、代碼片段
<% Java代碼或腳本代碼 %>

通過Java代碼可以定義變量或流程控制語句,通過腳本代碼可以應用jsp內置對象在頁面輸出內容、處理請求和響應、訪問session會話等;

4、聲明標識和代碼片段的區別:
兩者聲明的變量和方法都是在當前頁面有效,區別在於聲明週期不同;
聲明標識聲明的變量和方法的聲明週期是從創建開始到服務器關閉結束;
代碼片段聲明週期更短,頁面關閉後就會銷燬;


JSP 3大指令

指令標識主要用於設置整個JSP頁面範圍內有效的相關信息,它是被服務器解釋並執行的,不會產生任何輸出到頁面中的內容,也就是說指令標識對客戶端瀏覽器是不可見的;

<%@ 指令 屬性名=“屬性值” %>

  • page指令:設置整個JSP頁面的一些屬性信息;

    • language:設置當前頁面使用的語言;
    • extends:指定JSP頁面繼承的父類;
    • pageEncoding:指定當前JSP頁面的編碼格式;
    • contentType:指定的是響應給客戶端時使用的編碼,瀏覽器會據此顯示頁面內容;
    • import:導入當前頁面中用到的類包,供嵌入的Java代碼使用;
    • session:指定jsp頁面是否使用http的session會話對象;默認true,jsp頁面可以使用http的session對象;
    • buffer:設置jsp的out對象使用的緩衝區的大小;
    • autoFlush:指定當前緩衝區已滿時,自動將緩衝區的內容輸出到客戶端;默認true;false時表示緩衝區滿時拋出異常;
    • errorPage:指定處理當前JSP頁面異常錯誤的另一個jsp頁面;指定的jsp錯誤處理頁面的isErrorPage屬性必須設置爲true;errorPage屬性的值是一個url字符串;
    • isErrorPage:表示當前頁面作爲錯誤處理頁面來處理另一個jsp頁面的錯誤;
  • include指令:在當前頁面中 靜態包含另一個JSP頁面;

  • taglib指令:聲明當前頁面中所使用的標籤庫,同時引用標籤庫,並指定標籤庫的前綴;在頁面引入標籤庫之後,就可以通過前綴來引用標籤庫中的標籤了;


JSP 7大動作

動作標識在請求處理階段按照在jsp頁面中出現的順序執行,用於操作JavaBean、包含其他文件、請求轉發等;

動作標識是jsp內置的標籤庫;

  • jsp:include:動態包含一個JSP文件;先編譯被包含的文件,然後再寫入到包含的文件中;

    • page屬性:指定被包含文件的相對路徑
    • flush:默認false;設爲true時,在當前頁面輸出使用了緩衝區的情況下,先刷新緩衝區,再執行包含工作;
  • jsp:forward:執行請求轉發,把請求轉到其他web資源:另一個jsp頁面、HTML、servlet等;執行請求轉發後,當前頁面不再被執行,而是去執行該標識指定的目標頁面,然後從目標頁面返回結果;

    • page屬性:指定請求轉發的目標頁面;請求被轉向的目標文件必須是當前應用中的資源;
  • jsp:param:可以作爲其他標識的子標識,用於爲其他標識傳遞參數;
    <jsp:param name="參數名" value="參數值" />
    通過該標識指定的參數將以“參數名=參數值”的形式加入到請求中,功能與在文件名後面直接加“?參數名=參數值”相同;

  • jsp:useBean:在jsp頁面中實例化一個 JavaBean;若在指定範圍內已經有了一個指定的實例,那麼就直接使用已經存在的,不再新創建實例了;
    <jsp:useNean id="實例名" scope="實例作用域" class="完整類名" />

  • jsp:setProperty:設置 JavaBean 的屬性;
    <jsp:setProperty name="實例名" property="屬性名" value="屬性值" />

  • jsp:getProperty:獲取 JavaBean 的屬性;
    <jsp:getProperty name="實例名" property="屬性名" />


JSP 9大內置對象

jsp 有哪些內置對象?作用分別是什麼?分別有什麼方法?

  • request:表示HttpServletRequest對象;包含客戶端瀏覽器的請求信息,主要用來接收通過Http協議傳送到服務器的數據;
    request對象的作用域是一次請求;
    提供了一些用來獲取請求信息的方法:getParameter()、set/getAttribute、getCookies()、getSession()、getHeader();

  • response:表示 HttpServletResponse 對象;包含對客戶端的響應信息,主要用來將響應信息傳回客戶端;
    方法:addCookie、addHeader、setCharactorEncoding
    request對象的作用域是一次請求;

  • session:用來存儲用戶的狀態信息;
    從客戶端打開瀏覽器並連接到瀏覽器開始,到關閉瀏覽器離開服務器結束,是一個session,JSP引擎會在首次訪問服務器上的JSP頁面時創建Session對象,並分配一個String類型的ID號,然後將ID號返回給客戶端存放在Cookie裏;在一個session內,客戶端在服務器的幾個頁面之間跳轉,服務器都會知道是一個客戶在操作;
    方法:set/getAttribute、getID、

  • application:保存可以在所有客戶之間共享的數據信息;application對象保存的保存的信息在整個應用中有效,比session對象的生命週期更長;
    服務器啓動後就會產生Application對象,所有訪問服務器的用戶共享這個對象及對象中存放的數據,直到服務器關閉,找個對象才消失;
    方法:set/getAttribute

  • pageContext:表示JSP頁面編譯後的內容,通過這個對象,可以獲取到其他8個內置對象;
    作用域:page範圍有效;

  • out:用來向瀏覽器輸出信息;
    out.print():輸出各種類型數據;
    out.newLine():輸出一個換行符;
    out.close():關閉流

  • page:表示JSP頁面本身;是JSP編譯成Servlet類的對象,相當於普通Java類的this;

  • config:主要用來獲取服務器的配置信息;

  • Exception:封裝了JSP頁面拋出的異常信息,經常被用來處理錯誤頁面;


同一應用中頁面間傳值有哪些方式

(1)使用get方式:直接在URL請求後添加***.jsp?key=value
(2)使用post方式:通過表單提交;
(3)使用request、response、session等傳遞:request.setAttribute()、request.getAttribute();


JSP如何獲取HTML FORM中的數據;

html:
	<form action="a.jsp">
		<input type="text" name="test_data" />
		<input type="submit" value="提交" />
	</form>
jsp:
	<% String testData = request.getParameter("tets_data") %>

在JSP中如何使用JavaBeans,jsp中如何使用一個已經定義好的類;

<jsp:useBean id="實例的名稱" class="指定類的完整包名" scope="實例的作用域" />   // 實例化一個類對象;
<jsp:setProperty name="實例的名稱" property="類的屬性名" value="要賦的值" />  // 調用set方法給屬性賦值;
<jsp:getProperty name="實例的名稱" property="類的屬性名" />    // 調用get方法獲取屬性的值;JSP頁面最導入類包:<%@ page  import="Java類所在包"  %>JSP頁面中使用Java類:<%  Java代碼 %>

JSP中動態include和靜態include區別

靜態包含使用jsp指令實現:<%@include file=".jsp" %>
動態包含使用jsp動作實現:<jsp:include page="
.jsp">

靜態包含相當於把被包含頁面的內容直接複製到包含頁面,然後再編譯成Servlet;兩個JSP頁面不能有同名參數;只會產生一個class文件;

動態包含是先編譯被包含頁面,然後再將頁面的結果寫入到包含的頁面中,兩個jsp頁面中可以有同名參數;會產生多個class文件;


頁面間對象傳遞的方法

request,session,application,cookie 等


Session 與 cookie

網頁之間是通過Http協議傳輸數據的,而Http是無狀態的協議,也就是說一旦數據提交完後,瀏覽器和服務器的連接就會關閉,再次交互的時候需要重新建立新的連接;

http是無狀態的,服務器無法確認用戶的信息;爲了解決這個問題,服務器會給每個訪問服務器的用戶發一個通行證,用戶再次訪問服務器的時候需要攜帶通行證,這樣服務器就可以通過通行證確認用戶的信息;

Cookie是客戶端的通行證;是有服務器發送給客戶端的特殊信息,以文本的形式存放在客戶端;客戶端每次向服務器發送請求 都會帶上這些信息,cookie存放在HTTP請求頭中;服務器接收到請求,會解析cookie,生成與客戶端對應的內容;

Session是服務端的機制,在服務器上保存的信息;當服務器需要爲客戶端的請求創建session的時候,首先檢查這個請求中是否包含了session標識:sessionID,如果有,說明以前已經爲此客戶端創建過session,服務器就按照sessionID檢索出這個session直接使用,若客戶端請求不包含sessionID,則創建一個session,並生成一個與此客戶端相關的sessionID,然後在響應的時候,同響應數據發送給客戶端進行保存;

Session實現方式:
(1)使用Cookie實現:服務器給每個session分配一個唯一的JSessionID,並通過Cookie發送給客戶端,當客戶端發起新的請求的時候,將在消息頭中攜帶這個JSessionID,這樣服務器就據此找到客戶端對應的Session;

(2)使用URL回寫來實現:服務器在發送給瀏覽器頁面的所有連接中,都攜帶JSessionID的參數,這樣客戶端點擊任何一個連接,都會把JSessionID帶回服務器;如果直接在瀏覽器輸入服務端資源的URL來請求該資源,Session是體會不到的;

Tomcat對Session的實現是同時使用Cookie和URL回寫機制,如果發現客戶端支持Cookie,就繼續使用Cookie,停止使用URL回寫,若Cookie被禁用,就使用URL回寫機制;

Session 與 Cookie區別:
1、Coolie只能存儲字符串,如果要存儲非 ASCII碼的字符串,還要對其編碼;Session可以存儲任何類型的數據;

2、Cookie存儲在客戶端瀏覽器上,對客戶端是可見的,信息容易泄露,最好對Cookie加密;
Session存儲在服務器,對客戶端不可見,信息是安全的;

3、Session保存在服務器上,每個用戶都會產生一個Session,如果併發訪問的用戶多,會消耗大量的內存,影響服務器的性能;
Cookie是保存在客戶端的,不佔服務器的資源;

4、如果瀏覽器禁用了Cookie,Cookie就沒法用了;但是Session可以通過URL地址重寫來進行會話追蹤;


MVC 的各個部分都有那些技術來實現?如何實現?

Model:模型層,;JavaBean、Hibernate、Mybatis;

View:視圖層;JSP、HTML;

Controller:控制層;Servlet、SpringMVC;


SQL

主鍵

主鍵:是數據庫表中能唯一標識一條記錄的一個或多個屬性的集合;
主鍵只能有一個,並且不能爲空;

作用:保證數據的完整性,加快數據庫的操作速度;

外鍵:表的外鍵是另一張表的主鍵;
外鍵可以有多個,可以爲空;
作用:可以將一張表與其他表關聯起來;

外鍵

索引

視圖

存儲過程

DQL、DML、DDL、DCL

1、DQL 數據查詢語言:
基本結構是select子句、from子句、where子句組成的查詢塊;

2、DML 數據操縱語言:
主要是對數據庫進行一些操作:insert、update、delete、merge、insert(無處安放,所以放在dml中);

3、DDL 數據定義語言:
用來創建/修改數據庫的各種對象:表、視圖、索引、同義詞、聚集等;
create/alter/drop/truncate table/view/index/syn/cluster ...

DDL操作是隱式提交的,不能rollback;

4、DCL 數據控制語言:
用來授權或回收訪問數據庫的某種特權,並控制數據庫控制事務發生的時間及效果,對數據實行監視等;
grant授權、revoke收回授權

5、TCL 事務控制語言:
commit提交、rollback回滾、savepoint保存點;

優化

1、union和union all區別:
union:用於合併兩個select語句的結果集;兩個select語句查詢的列數量、順序要相同,列的類型要相似;

union合併兩個結果集時,將重複的行去除,union all不去除重複的記錄;

2、drop、truncate:
drop刪除表內容和表結構;
truncate只刪除表內容,保留表結構;

3、<>、<=>
<>:是不等於;
<=>:類似=,用於比較兩個值,不同的是,=不能操作NULL,<=>可以向NULL與某個值進行比較;
<=>和IS NULL、IS NOT NULL功能類似;

4、having:過濾;
where子句不能和合計函數一起使用,所以使用having進行過濾;

5、in、exists:
select * from a where a.id in (select id from b);
select * from a where exists(select id from b);

in:先執行子查詢in(),將查詢結果緩存起來,然後將a表中的記錄分別於緩存結果進行比較;相當於兩層循環:外層循環a表,內層循環遍歷in()結果緩存;緩存數量較大時,不適用;

exists:循環遍歷外表a,看記錄是否在exists()中存在,exists會執行a.len次,但是不緩存結果集;


Linux

常用命令

1、在指定目錄下查找特定文件:find

  • find -name fileName:從當前目錄 遞歸查找指定文件;精確查找
  • find -iname fileName:-iname表示文件名忽略大小寫;不區分大小寫
  • find / -name fileName:從linux的根目錄/下開始, 遞歸查找指定文件;模糊查找
  • find ~ -name "*.txt":從當前用戶的home目錄下查找指定文件;

2、文本檢索 - 根據文件內容檢索,在指定的文本文件中查找指定字符串:grep

  • grep "要搜索的字符串" "指定查找的文件範圍"
  • grep "要搜索的字符串":不指定文件範圍,會從標準輸入設備(控制檯)讀取數據;
  • -o:只輸出符合正則表達式的字符串;
  • -v:過濾掉包含相關字符串的內容;
  • grep:一次只能搜索一個指定的模式;
  • egrep:等同於grep -E,可以使用擴展的字符串模式進行搜索;
  • fgrep:等同於grep -F,是快速搜索命令,它檢索固定字符串,但是不識別正則表達式;

3、管道操作符|:將指令連接起來,前一個指令的輸出作爲後一個指令的輸入;
- 管道操作符只能處理前一個命令的正確輸出,不處理錯誤輸出;
- 右邊命令必須能夠接收標準輸入流,否則傳遞過程中數據會被拋棄;
- 常用來作爲接收管道數據的命令有:sed、awk、grep、cut、head、top、less、more、wc、join、sort、split等;

4、統計文件內容:awk

  • 一次讀取一行文本,按輸入分割符進行切片,切成多個組成部分;將每個分片直接保存在內建的變量中,$1,$2,…($0表示行的全部內容),引用指定的變量可以顯示指定的切片的內容;支持對單個切片的判斷,支持對所有切片進行循環判斷,默認分隔符爲空格;
  • awk '{print $1,$4}' fileName1 fileName2:獲取指定文件的第一列和第四列的內容;逐行遍歷文本內容,將每行的各個部分按照空格切片出來,進行保存;
  • awk '$1=="aa" && $2==12{print $0} fileName’:獲取指定文件第一列值爲"aa",第二列值爲12的行的所有內容;
  • awk '($1=="aa" && $2==12) || NR==1 {print $0} fileName':顯示錶頭內容;NR表示從awk開始執行後,按照記錄分隔符,讀取的數據的次數,默認的記錄分隔符爲換行符,因此NR默認讀取的就是數據的行數;
  • awk -F "," '{print $2}' fileName:指定切片分隔符;
  • grep 'str' fileName | awk '{arrStr[$1]++}END{for(i in arrStr) print i "\t" arrStr[i]}':定義一個數組,用它的下標來保存行切片的內容,數組的值就對應行切片的值,進行累加,END表示掃描結束需要做什麼操作,就是執行花括號裏面的內容:遍歷數組裏面的內容;

5、批量替換/刪除文件內容:sed

  • stream editor,流編輯器,適用於對文本的行內容進行處理、編輯;
  • sed -i 's/^zhang/Zhang/g' fileName:將以zhang開頭的的字符串,改成Zhang;
    -i:直接在目標文本中做修改;默認是將修改後的內容輸出到終端;
    s:表示要進行字符串的操作;
    第一個反斜槓後面:要被替換的內容,^表示篩選出以xx開頭行;'s/./str//str/':表示以xx結尾;斜槓表示轉義特殊字符;
    第二個反斜槓後面:要替換成的目標內容;
    第三個反斜槓後面:不寫g,默認替換每一行第一次匹配的內容;g表示替換所有;
  • set -i 's/\.$/\;/g':將以.結尾的行的.替換成;
  • set -i '/^ *$/d':刪除空行;
  • set -i '/str/d':刪除包含str的行;

簡答題

1、在登錄Linux時,一個具有唯一進程ID號的shell將被調用,這個ID是什麼:PID

2、命令查看用戶tty終端信息:ps -ef | grep tomcat

3、下面那個用戶存放用戶密碼信息:/etc

4、文件權限讀、寫、執行三種符號的標誌依次是:rwx

5、某文件的組外成員的權限是隻讀、屬主是全部權限、組內權限是可讀可寫、該文件權限爲:764 (屬主/組內/其他)

6、改變文件的屬主的命令是:chown

7、解壓縮文件mydjango.tar.gz,我們可以用:tar -xzvf mydjango.tar.gz

8、檢查linux是否安裝了,可用哪些命令:rpm -q nginx
注意rpm -qi只能查詢用yum安裝的軟件,編譯的查不到

9、Linux配置文件一般放在什麼目錄:/etc

10、linux中查看內存,交換內存的情況命令是:free

11、觀察系統動態進程的命令是:top

12、如果執行命令,chmod 746 file.txt ,那麼該文件的權限是:rwxr—rw-

13、找出當前目錄以及其子目錄所有擴展名爲”.txt”的文件,那麼命令是:find -name "*.txt"

14、什麼命令常用於檢測網絡主機是否可達:ping

15、退出交互式shell,應該輸入什麼:exit

16、在父目錄不存在的時候,添加的參數是:-p

17、下列文件中,包含了主機名到IP地址映射關係的文件是:、etc/hosts

18、請問你使用的linux發行版是什麼?如何查看linux發行版信息:cat /etc/os-release

19、vim有幾種工作模式:命令模式、編輯模式、底線命令模式

20、如何解壓縮後綴是.gz文件:gzip -d *.gz
21、如何解壓縮後綴是.tar文件:tar -xf .tar
22、如何解壓縮後綴是.xz文件:xz -d .xz

23、www服務在internet最爲廣泛,採用的結構是:Browser/Server

24、如何給linux添加dns服務器記錄:在/etc/resolv.conf文件中添加nameserver dns服務器ip地址

25、每月的5,15,25的晚上5點50重啓nginx:
ctontab -e
50 17 5,15,25 /usr/bin/systemctl restart nginx
50 17 5,15,25 /opt/nginx112/sbin/nginx -s reload

26、每分鐘清空/tmp/內容:/usr/bin/rm -rf /tmp/*
27、每天早上6.30清空/tmp/的內容:30 6 /usr/bin/rm -rf /tmp/

28、每個星期三的下午6點和8點的第5到15分鐘之間備份mysql數據到/opt/:5-15 18,20 3 /usr/bin/cp -r /var/lib/mysql /opt/

29、centos版本系統服務管理命令是:servicesystemctl

30、如何遠程登錄阿里雲123.206.16.61:ssh [email protected]

31、備份數據庫文件:mysqldump -root -p 數據庫名 > 備份文件名

32、無法使用rm,使用提示’禁止你使用rm’,是爲什麼:配置別名alias

33、如何修改test.py屬組爲alex:chgrp alex test.py

34、如何在windows和linux傳輸文件?有哪些方法:xftp lrzsz scp

35、簡述dns解析流程?訪問www.pythonav.cn的解析流程:

  • 1.優先查找本地dns緩存
  • 2.查找本地/etc/hosts文件,是否有強制解析
  • 3.如果沒有去/etc/resolv.conf指定的dns服務器中查找記錄(需聯網
  • 4.在dns服務器中找到解析記錄後,在本地dns中添加緩存
  • 5.完成一次dns解析;

36、Linux安裝軟件方式:yum、rpm、源碼包

37、如何保證本地測試環境和線上開發環境一致性:docker打包鏡像

38、配置linux軟連接的命令:ln -s 目標文件名 軟連接名

39、如何永久添加/opt/python36/的環境變量:
vim /etc/profile
添加PATH = /opt/python36/bin:
source /etc/profile

40、給如下代碼添加註釋:

server{ # 一個虛擬主機
	listen 80; # 監聽的端口,訪問的端口80
	server_name 192.168.11.11; # 訪問的域名192.168.11.11
	location / { # 訪問的路徑 /
		root html; # 指定頁面的目錄,訪問/會找到html目錄
		index index.html # 指定網頁,訪問/就是訪問index.html
		}
}

41、使用rm -i 系統會提示什麼信息:是否真的刪除

42、爲何說rm -rf 慎用? -r遞歸刪除 -f強制刪除

43、如果端口8080被佔用,如何查看是什麼進程? netstat -tunlp | grep 8080

44、linux下載軟件包的方法有:wget curl

45、windows和linux常用遠程連接工具有哪些:xshell、putty、secure crt

46、如何給與一個腳本可執行權限:chmod u+x file

47、過濾出settings.py中所有的空白和註釋行:grep -v “^#” file |grep -v “^$”

48、過濾出file1中以abc結尾的行:grep “abc$” file1

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章