1 什麼是構造函數、構造代碼塊、靜態代碼塊?分別的作用是什麼?三者執行順序?
1 構造函數:
構造函數是一種特殊的函數。其主要功能是用來在創建對象時初始化對象, 即爲對象成員變量賦初始值,總與new運算符一起使用在創建對象的語句中。構造函數與類名相同,可重載多個不同的構造函數。
注:
1.構造函數的命名必須和類名完全相同。在java中普通函數可以和構造函數同名,但是必須帶有返回值;
2.構造函數的功能主要用於在類的對象創建時定義初始化的狀態。它沒有返回值,也不能用void來修飾。這就保證了它不僅什麼也不用自動返回,而且根本不能有任何選擇。而其他方法都有返回值,即使是void返回值。儘管方法體本身不會自動返回什麼,但仍然可以讓它返回一些東西,而這些東西可能是不安全的;
3.構造函數不能被直接調用,必須通過new運算符在創建對象時纔會自動調用;而一般的方法是在程序執行到它的時候被調用的;
4.當定義一個類的時候,通常情況下都會顯示該類的構造函數,並在函數中指定初始化的工作也可省略,不過Java編譯器會提供一個默認的構造函數.此默認構造函數是不帶參數的。而一般的方法不存在這一特點。
2 構造代碼塊:
構造代碼塊的作用和構造函數類似可以完成類中的成員變量進行初始化也可以調用成員方法
①、格式
在java類中使用{}聲明的代碼塊(和靜態代碼塊的區別是少了static關鍵字):
public class CodeBlock {
static{
System.out.println(“靜態代碼塊”);
}
{
System.out.println(“構造代碼塊”);
}
}
②、執行時機
構造代碼塊在創建對象時被調用,每次創建對象都會調用一次,但是優先於構造函數執行。需要注意的是,聽名字我們就知道,構造代碼塊不是優先於構造函數執行,而是依託於構造函數,也就是說,如果你不實例化對象,構造代碼塊是不會執行的。怎麼理解呢?我們看看下面這段代碼:
public class CodeBlock {
{
System.out.println(“構造代碼塊”);
}
public CodeBlock(){
System.out.println("無參構造函數");
}
public CodeBlock(String str){
System.out.println("有參構造函數");
}
}
如果存在多個構造代碼塊,則執行順序按照書寫順序依次執行。
③、構造代碼塊的作用
和構造函數的作用類似,都能對對象進行初始化,並且只要創建一個對象,構造代碼塊都會執行一次。但是反過來,構造函數則不一定每個對象建立時都執行(多個構造函數情況下,建立對象時傳入的參數不同則初始化使用對應的構造函數)。
利用每次創建對象的時候都會提前調用一次構造代碼塊特性,我們可以做諸如統計創建對象的次數等功能。
3 靜態代碼塊:
靜態代碼塊屬於類的,靜態代碼塊可以隨着類的加載而加載可以初始化中的靜態成員變量,也可以在連接JBCD時用於讀取文件中的連接信息
①、格式
在java類中(方法中不能存在靜態代碼塊)使用static關鍵字和{}聲明的代碼塊:
public class CodeBlock {
static{
System.out.println(“靜態代碼塊”);
}
}
②、執行時機
靜態代碼塊在類被加載的時候就運行了,而且只運行一次,並且優先於各種代碼塊以及構造函數。如果一個類中有多個靜態代碼塊,會按照書寫順序依次執行。後面在比較的時候會通過具體實例來證明。
③、靜態代碼塊的作用
一般情況下,如果有些代碼需要在項目啓動的時候就執行,這時候就需要靜態代碼塊。比如一個項目啓動需要加載的很多配置文件等資源,我們就可以都放入靜態代碼塊中。
執行順序:
靜態代碼塊>構造代碼塊>構造函數>普通代碼塊
2 double和Double區別?
1、Double是java定義的類,而double是預定義數據類型(8種中的一種)
2、Double就好比是對double類型的封裝,內置很多方法可以實現String到double的轉換,以及獲取各種double類型的屬性值(MAX_VALUE、SIZE等等)
注:
基於上述兩點,如果你只是普通的定義一個浮點類型的數據,兩者都可以,但是Double是類所以其對象是可以爲NULL的,而double定義的不能爲NULL,如果你要一些數字字符串,那麼就應該使用Double類型了,其內部幫你實現了強轉。
Double類型是double的包裝類,在JDK1.5以後,二者可以直接相互賦值,稱爲自動拆箱和自動裝箱。
3 2<<3等於多少?
2左移三位等於8
4 判斷字符串爲空時如何避免空指針異常?
判斷一個字符串str不爲空的方法有:
-
str!=null;
-
“”.equals(str);
-
str.length()!=0;
例子:
String str1 = null; str引用爲空
String str2 = ""; str應用一個空串
str1還不是一個實例化的對象,而str2已經實例化。
對象用equals比較,null用等號比較。
如果str1=null;下面的寫法錯誤:
if (str1.equals("") || str1==null) {//如果這樣寫就會報空指針異常錯誤
}
正確的寫法是
if(str1 == null || str1.equals("")) { //先判斷是不是對象,如果是,再判斷是不是空字符串
}
說明:
1 null表示這個字符串不指向任何的東西,如果這時候你調用它的方法,那麼就會出現空指針異常。
2 "“表示它指向一個長度爲0的字符串,這時候調用它的方法是安全的。
3 null不是對象,”“是對象,所以null沒有分配空間,”"分配了空間,例如:
5 什麼是重載和重寫
定義:
重載
簡單說,就是函數或者方法有同樣的名稱,但是參數列表不相同的情形(方法名相同,參數列表不同),這樣的同名不同參數的函數或者方法之間,互相稱之爲重載函數或者方法。
重寫
重寫指的是在Java的子類與父類中有兩個名稱、參數列表都相同的方法的情況。由於他們具有相同的方法簽名,所以子類中的新方法將覆蓋父類中原有的方法。
區別:
首先,重載和重寫都是多態的一種體現方式。
重載是編譯期間的活動,重寫是運行期間的活動。
其次,重載是在一個類中定義相同的名字的方法,方法的參數列表或者類型要互相不同,但是返回值類型不作爲是否重載的標準,可以修改可見性;
重寫是不同的,要求子類重寫基類的方法時要與父類方法具有相同的參數類型和返回值,可見性需要大於等於基類的方法。
6 實現線程的三種方式
java中實現多線程有三種方法:
繼承Thread類
實現Runnable接口
實現Callable接口
一、Java中創建線程主要有三種方式:
1、繼承Thread類創建線程類
(1)定義Thread類的子類,並重寫該類的run方法,該run方法的方法體就代表了線程要完成的任務。因此把run()方法稱爲執行體。
(2)創建Thread子類的實例,即創建了線程對象。
(3)調用線程對象的start()方法來啓動該線程。
2、通過Runnable接口創建線程類
(1)定義runnable接口的實現類,並重寫該接口的run()方法,該run()方法的方法體同樣是該線程的線程執行體。
(2)創建 Runnable實現類的實例,並以此實例作爲Thread的target來創建Thread對象,該Thread對象纔是真正的線程對象。
(3)調用線程對象的start()方法來啓動該線程。
3、通過Callable和Future創建線程
(1)創建Callable接口的實現類,並實現call()方法,該call()方法將作爲線程執行體,並且有返回值。
public interface Callable
{
V call() throws Exception;
}
(2)創建Callable實現類的實例,使用FutureTask類來包裝Callable對象,該FutureTask對象封裝了該Callable對象的call()方法的返回值。(FutureTask是一個包裝器,它通過接受Callable來創建,它同時實現了Future和Runnable接口。)
(3)使用FutureTask對象作爲Thread對象的target創建並啓動新線程。
(4)調用FutureTask對象的get()方法來獲得子線程執行結束後的返回值
二、創建線程的三種方式的對比
1、採用實現Runnable、Callable接口的方式創建多線程時,
優勢是:
線程類只是實現了Runnable接口或Callable接口,還可以繼承其他類。
在這種方式下,多個線程可以共享同一個target對象,所以非常適合多個相同線程來處理同一份資源的情況,從而可以將CPU、代碼和數據分開,形成清晰的模型,較好地體現了面向對象的思想。
劣勢是:
編程稍微複雜,如果要訪問當前線程,則必須使用Thread.currentThread()方法。
2、使用繼承Thread類的方式創建多線程時,
優勢是:
編寫簡單,如果需要訪問當前線程,則無需使用Thread.currentThread()方法,直接使用this即可獲得當前線程。
劣勢是:
線程類已經繼承了Thread類,所以不能再繼承其他父類。
3、Runnable和Callable的區別
(1) Callable規定(重寫)的方法是call(),Runnable規定(重寫)的方法是run()。
(2) Callable的任務執行後可返回值,而Runnable的任務是不能返回值的。
(3) call方法可以拋出異常,run方法不可以。
(4) 運行Callable任務可以拿到一個Future對象,表示異步計算的結果。它提供了檢查計算是否完成的方法,以等待計算的完成,並檢索計算的結果。通過Future對象可以瞭解任務執行情況,可取消任務的執行,還可獲取執行結果。
7 Thread類中的start()和run()方法有什麼區別
區別:
run()方法:在本線程內調用該Runnable對象的run()方法,可以重複多次調用;
start()方法:啓動一個線程,調用該Runnable對象的run()方法,不能多次啓動一個線程;
注:
start()方法被用來啓動新創建的線程,而且start()內部調用了run()方法。
8 final修飾的方法可以被繼承/重載/重寫嗎?
final表示最終的意思,它修飾的類是不能被繼承的;final修飾的方法能被繼承(Math類裏就有),但是不能夠被重寫。其實關係並不複雜,你只需要記住這樣一句話:final可用於聲明屬性、方法和類,分別表示屬性不可變,方法不可重寫,類不可繼承。
-
final修飾的類,爲最終類,該類不能被繼承。如String 類
-
final修飾的方法可以被繼承和重載,但不能被重寫
-
final修飾的變量不能被修改,是個常量
9 遍歷二叉樹的方式有哪些?選擇一種方法用程序實現
二叉樹的遍歷分爲三種:前序遍歷 中序遍歷 後序遍歷
前序遍歷:按照“根左右”,先遍歷根節點,再遍歷左子樹 ,再遍歷右子樹
中序遍歷:按照“左根右“,先遍歷左子樹,再遍歷根節點,最後遍歷右子樹
後續遍歷:按照“左右根”,先遍歷左子樹,再遍歷右子樹,最後遍歷根節點
其中前,後,中指的是每次遍歷時候的根節點被遍歷的順序
import java.util.ArrayList;
import java.util.List;
public class Tree {
private Node root;
private List<Node> list=new ArrayList<Node>();
public Tree(){
init();
}
//樹的初始化:先從葉節點開始,由葉到根
public void init(){
Node x=new Node("X",null,null);
Node y=new Node("Y",null,null);
Node d=new Node("d",x,y);
Node e=new Node("e",null,null);
Node f=new Node("f",null,null);
Node c=new Node("c",e,f);
Node b=new Node("b",d,null);
Node a=new Node("a",b,c);
root =a;
}
//定義節點類:
private class Node{
private String data;
private Node lchid;//定義指向左子樹的指針
private Node rchild;//定義指向右子樹的指針
public Node(String data,Node lchild,Node rchild){
this.data=data;
this.lchid=lchild;
this.rchild=rchild;
}
}
/**
* 對該二叉樹進行前序遍歷 結果存儲到list中 前序遍歷:ABDXYCEF
*/
public void preOrder(Node node)
{
list.add(node); //先將根節點存入list
//如果左子樹不爲空繼續往左找,在遞歸調用方法的時候一直會將子樹的根存入list,這就做到了先遍歷根節點
if(node.lchid != null)
{
preOrder(node.lchid);
}
//無論走到哪一層,只要當前節點左子樹爲空,那麼就可以在右子樹上遍歷,保證了根左右的遍歷順序
if(node.rchild != null)
{
preOrder(node.rchild);
}
}
/**
* 對該二叉樹進行中序遍歷 結果存儲到list中,中序結果XdYbaecf
*/
public void inOrder(Node node)
{
if(node.lchid!=null){
inOrder(node.lchid);
}
list.add(node);
if(node.rchild!=null){
inOrder(node.rchild);
}
}
/**
* 對該二叉樹進行後序遍歷 結果存儲到list中,後續結果:XYdbefca
*/
public void postOrder(Node node)
{
if(node.lchid!=null){
postOrder(node.lchid);
}
if(node.rchild!=null){
postOrder(node.rchild);
}
list.add(node);
}
/**
* 返回當前數的深度
* 說明:
* 1、如果一棵樹只有一個結點,它的深度爲1。
* 2、如果根結點只有左子樹而沒有右子樹,那麼樹的深度是其左子樹的深度加1;
* 3、如果根結點只有右子樹而沒有左子樹,那麼樹的深度應該是其右子樹的深度加1;
* 4、如果既有右子樹又有左子樹,那該樹的深度就是其左、右子樹深度的較大值再加1。
*
* @return
*/
public int getTreeDepth(Node node) {
if(node.lchid == null && node.rchild == null)
{
return 1;
}
int left=0,right = 0;
if(node.lchid!=null)
{
left = getTreeDepth(node.lchid);
}
if(node.rchild!=null)
{
right = getTreeDepth(node.rchild);
}
return left>right?left+1:right+1;
}
//得到遍歷結果
public List<Node> getResult()
{
return list;
}
public static void main(String[] args) {
Tree tree=new Tree();
System.out.println("根節點是:"+tree.root);
//tree.preOrder(tree.root);
tree.postOrder(tree.root);
for(Node node:tree.getResult()){
System.out.println(node.data);
}
System.out.println("樹的深度是"+tree.getTreeDepth(tree.root));
}
}
10 java常見的排序方法有哪些?選擇一種實現
鏈接:https://blog.csdn.net/qq_23994787/article/details/77965750
11 什麼是反射?如何通過反射訪問私有字段?
官方解釋:
JAVA反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法,對於任意一個對象,都能夠調用它的任意一個方法和屬性,這種動態獲取的信息以及動態調用對象的方法的功能稱爲java語言的反射機制。
訪問私有字段:
爲了訪問私有字段,可以調用Class.getDeclaredField(String name)或者Class.getDeclaredFields()方法,並且需要開啓權限setAccessible(true)。方法Class.getField(String name)和Class.getFields()僅僅返回共有的字段,所以它們都無法起到作用。