【面試】【面試題】

1.JAVA 面試題

https://blog.csdn.net/linzhiqiang0316/article/details/80473906

https://blog.csdn.net/fanx_000/article/details/80297487

https://blog.csdn.net/hope900/article/details/78647466/

 

2.JD 面試

https://www.nowcoder.com/discuss/91413

 

JAVA

1.面向對象的三個特徵

封裝,繼承,多態

多態

多態指允許不同類的對象對同一“消息”做出響應。即同一消息可以根據發送對象的不同而採用多種不同的行爲方式。可以用於消除類型之間的耦合關係。

實現多態主要有以下三種方式:
1. 接口實現 
2. 繼承父類重寫方法 
3. 同一類中進行方法重載

 

2.java 創建對象的幾種方式

https://www.cnblogs.com/wxd0108/p/5685817.html

1.new:

   創建一個新類
   強類型,相對高效。能調用任何public構造

2.newInstance:

   類加載機制
  1.先調用Class加載方法 加載某個類;

  2.然後把加載的類實例化 。

  即所謂的反射機制
  通過反射創建對象 ,弱類型。低效率。只能調用無參構造:

   https://www.cnblogs.com/liuyanmin/p/5146557.html

             1.虛擬機裏面這個類已經加載;

             2.這個類已經連接了

 // 初始化 Context 類的對象
 ctor = wigigClass.getConstructor(Context.class);
 // 實例化這個對象
 wigigService = ctor.newInstance(context);
Method method =Class.forName("Android.os.ServiceManager") .getMethod("getService",String.class);

// 獲取遠程TELEPHONY_SERVICE的IBinder對象的代理

IBinder binder =(IBinder) method.invoke(null, new Object[] { TELEPHONY_SERVICE});

// 將IBinder對象的代理轉換爲ITelephony對象

ITelephonytelephony = ITelephony.Stub.asInterface(binder);

// 掛斷電話

telephony.endCall();

Binder 通信裏面就是newInstance 的

3.clone

jvm就會創建一個新的對象,將前面對象的內容全部拷貝進去。用clone方法創建對象並不會調用任何構造函數。

4.使用反序列化

當我們序列化和反序列化一個對象,jvm會給我們創建一個單獨的對象。在反序列化時,jvm創建對象並不會調用任何構造函數。
爲了反序列化一個對象,我們需要讓我們的類實現Serializable接口

例子
 

public class ObjectCreation {
    public static void main(String... args) throws Exception {
        // 1 new 創建
        Employee emp1 = new Employee();
        emp1.setName("Naresh");


        // 2  Class class類的newInstance() 方法
        Employee emp2 = (Employee)Class.forName("org.programming.mitra.exercises.Employee")
.newInstance();
        // 或者
        // Employee emp2 = Employee.class.newInstance();
        emp2.setName("Rishi");


        // 3  Constructor class類的 newInstance() 方法
        Constructor<Employee> constructor = Employee.class.getConstructor();
        Employee emp3 = constructor.newInstance();
        emp3.setName("Yogesh");


        // 4 clone() 方法
        Employee emp4 = (Employee) emp3.clone();
        emp4.setName("Atul");


        // 5 Serialization
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("data.obj"));
        out.writeObject(emp4);
        out.close();

        //Deserialization
        ObjectInputStream in = new ObjectInputStream(new FileInputStream("data.obj"));
        Employee emp5 = (Employee) in.readObject();
        in.close();
        emp5.setName("Akash");
        System.out.println(emp5 + ", hashcode : " + emp5.hashCode());
    }
}

3.static , final , static final 的區別

1.static

static修飾的類成員,獨立於該類的任何實例化的對象,在程序運行過程中,其內存僅被初始化一次,但被類的所有實例共享。

    1.不加static修飾的成員是對象成員,歸每個對象所有。

    2.加static修飾的成員是類成員,可以由一個類直接調用,爲所有對象共有。

用static關鍵字標識的程序元素是靜態的,在此類被加載時初始化。

static 方法:在不創建此類的對象的情況下調用某個方法,就可以將這個方法設置爲static,靜態方法裏只能調用靜態變量。

2. final

1.用final關鍵字修飾的變量,只能進行一次賦值操作,並且在生存期內不可以改變它的值。

   1. 修飾數據:數據無法被修改;

   2. 修飾方法:方法無法被覆蓋,但是可以被繼承;

  3.  修飾類 :  類無法被繼承;

final有哪些用法


1.被final修飾的類不可以被繼承 
2.被final修飾的方法不可以被重寫 
3.被final修飾的變量不可以被改變。如果修飾引用,那麼表示引用不可變,引用指向的內容可變。
4.被final修飾的方法,JVM會嘗試將其內聯,以提高運行效率 
5.被final修飾的常量,在編譯階段會存入常量池中。

回答出編譯器對final域要遵守的兩個重排序規則更好:
1.在構造函數內對一個final域的寫入,與隨後把這個被構造對象的引用賦值給一個引用變量,這兩個操作之間不能重排序。
2.初次讀一個包含final域的對象的引用,與隨後初次讀這個final域,這兩個操作之間不能重排序。

3.static final

同時使用static和final修飾的成員在內存中只佔據一段不能改變的存儲空間

4. public static final

public(類的子類也可以訪問) static (類的靜態成員,類的所有子類都可以訪問,無需初始化)final (無法被修改)

 

4. JAVA 創建線程的幾種方法

https://www.cnblogs.com/3s540/p/7172146.html

1.繼承Thread 類創建線程

    1.定義Thread類的子類並重寫該類的run() 方法, run()的方法體就是線程需要完成的任務;

    2.創建Thread子類的實例,也就是創建線程對象;

    3.啓動線程,調用線程是start() 方法;

2.實現Runable接口創建線程

    1.定義Runable接口的實現類,重寫run() 方法,run()的方法體就是線程需要完成的任務;

    2.創建Runable 實現類的實例,並用這個實例作爲Thread 的target來創建Thread對象,這個Thread對象纔是真正的線程對象;

    3.調用線程對象的start()方法來啓動線程;

//實現Runnable接口
public class MyThread2 implements Runnable {

  public void run(){

  //重寫run方法

  }

}

public class Main {

  public static void main(String[] args){

    //創建線程實例
    MyThread2 myThread=new MyThread2();

       //實例作爲Thread 的target,初始化thread 對象
    Thread thread=new Thread(myThread);

       //啓動thread
    thread().start();

  }

}

 

*4.x* 繼承Thread 和實現Runable 的區別

 一般多線程環境下都是用 實現Runable 的方式來創建線程,利用Runable啓動線程中,只new 了一個實現Runable 接口的實例,多線程中,傳入Thread 的都是這個相同的實例,達到多線程下的數據共享

package multithreading;  
  
public class MyThreadWithImplements implements Runnable {  
  
    private int tickets = 10;  
  
    @Override  
    public void run() {  
  
        for (int i = 0; i <= 100; i++) {  
            if(tickets>0){  
                System.out.println(Thread.currentThread().getName()+"--賣出票:" + tickets--);  
            }  
        }  
    }  
      
      
    public static void main(String[] args) {  
        MyThreadWithImplements myRunnable = new MyThreadWithImplements();  
        Thread thread1 = new Thread(myRunnable, "窗口一");  
        Thread thread2 = new Thread(myRunnable, "窗口二");  
        Thread thread3 = new Thread(myRunnable, "窗口三");  
  
        thread1.start();  
        thread2.start();  
        thread3.start();  
    }  
  
}  

運行結果:

窗口二--賣出票:10
窗口三--賣出票:9
窗口一--賣出票:8
窗口三--賣出票:6
窗口三--賣出票:4
窗口三--賣出票:3
窗口三--賣出票:2
窗口三--賣出票:1
窗口二--賣出票:7
窗口一--賣出票:5

3.使用Callable和Future創建線程

 和Runnable接口不一樣,Callable接口提供了一個call()方法作爲線程執行體,call()方法比run()方法功能要強大

1.創建callable接口的實現類,並實現call() 方法,然後創建該類的實例;

2.使用FutureTask類來包裝Callable對象,該FutureTask對象封裝了Callable對象的call() 方法的返回值;

3.使用FutrueTask對象作爲Thread對象的target創建並啓動線程(因爲FutureTask實現了Runable接口);

4.調用FutureTask對象的get()方法來獲得子線程執行結束的返回值;

 

5. JAVA 反射機制

https://blog.csdn.net/sinat_38259539/article/details/71799078

PYJ: 虛擬機加載磁盤中每個XXX.class 文件時,都會在JVM 裏生成一個class 對象,反射就是通過獲取到此class 對象而反向的獲取對於的構造方法和參數相關。

運行時動態的獲取信息以及動態調用對象的方法的功能稱爲 Java 的反射機制

Class 類與 java.lang.reflect 類庫一起對反射的概念進行了支持,該類庫包含了 Field,Method,Constructor 類 (每個類都實現了 Member 接口)。這些類型的對象時由 JVM 在運行時創建的,用以表示未知類裏對應的成員

1.獲取字節碼(class 對象)

    (方法一)通過 Object 類中的 getClass() 方法,想要用這種方法必須要明確具體的類並且創建該類的對象。

    (方法二)所有數據類型都具備一個靜態的屬性,通過XXX.class 來獲取對應的 Class 對象。但是還是要明確到類,然後才能調用類中的靜態成員????

     (方法三)只要通過給定類的字符串名稱就可以獲取該類的字節碼對象,這樣做擴展性更強。通過 Class.forName() 方法完成,必須要指定類的全限定名

package fanshe;
/**
 * 獲取Class對象的三種方式
 * 1 Object ——> getClass();
 * 2 任何數據類型(包括基本數據類型)都有一個“靜態”的class屬性
 * 3 通過Class類的靜態方法:forName(String  className)(常用)
 *
 */
public class Fanshe {
	public static void main(String[] args) {
		//第一種方式獲取Class對象  
		Student stu1 = new Student();//這一new 產生一個Student對象,一個Class對象。
		Class stuClass = stu1.getClass();//獲取Class對象
		System.out.println(stuClass.getName());
		
		//第二種方式獲取Class對象
		Class stuClass2 = Student.class;
		System.out.println(stuClass == stuClass2);//判斷第一種方式獲取的Class對象和第二種方式獲取的是否是同一個
		
		//第三種方式獲取Class對象
		try {
			Class stuClass3 = Class.forName("fanshe.Student");//注意此字符串必須是真實路徑,就是帶包名的類路徑,包名.類名
			System.out.println(stuClass3 == stuClass2);//判斷三種方式是否獲取的是同一個Class對象
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		
	}
}

所有類都繼承Object類,Object.getClass()就是獲取此類的class 對象;

在運行期間,一個類,只有一個Class對象產生。

2.通過反射獲取構造方法

通過class 類的getConstructor 方法等獲取構造函數

//獲取到此類的class 對象
Class clazz = Class.forName("fanshe.Student");

//調用class.getConstructor 獲取無參構造方法
Constructor con = clazz.getConstructor(null);

//有參
con = clazz.getDeclaredConstructor(char.class)

3.例子

//1.獲取類的class對象

Class stuClass = Class.forName("fanshe.field.Student");

//2.stuClass.getConstructor -》獲取類的構造方法 , stuClass.getConstructor.newInstance生成student對應的Object 對象

Object obj = stuClass.getConstructor().newInstance();

//3.初始化student對象

Student stu = (Student)obj;

6.類的“替換”

Q: 怎麼在不修改A.class 文件的前提下,修改調用A 時 函數的輸出?

A: 創建一個B extends A ,在B.class 文件中複寫A.class 中定義的函數,在a =new A()時 ,改成 a = new B() , 利用JAVA 的多態特性,後續 對 a 的操作,實際都是操作B 類

 

7.父類的靜態方法能否被子類重寫

不可以。靜態方法是類被虛擬機 加載/運行 時就分配了存儲空間保存好了,子類只能調用,不能重寫

8.靜態變量和實例變量的區別?

靜態變量存儲在方法區,屬於類所有。

實例變量存儲在堆當中,其引用存在當前線程棧

9.能否創建一個包含可變對象的不可變對象?

????

10.switch中能否使用string做參數

可以。idk 1.7之後switch開始支持String

11. “==” 和 “equals”

https://blog.csdn.net/qauchangqingwei/article/details/80831797

1. n =3;
   m =3;
   n == m ---> true;

2. str = "hello";
   str2 = "hello";
   str == str2 ---> true 
// str 和str2 都指向同一字符常量“hello”的引用地址
// JAVA在編譯 str = "hello" 時,將“hello”放在常量池中,編譯“str2 = “hello” ” 時,會先檢查“hello”這個字符是否已經在常量池中,如果有,則直接把str2 指向之前創建的“hello”的常量池;

3. String str3=new String("hello");
   String str4=new String("hello");
   str3==str4 ---> false 
//"new" 關鍵字代表創建新的對象,每次調用,都會先創建新的對象,在對其進行賦值初始化
//-------------important--------------------------------------------
每當我們創建字符串常量時,JVM會首先檢查字符串常量池,如果該字符串已經存在常量池中,那麼就直接返回常量池中的實例引用。如果字符串不存在常量池中,就會實例化該字符串並且將其放到常量池中。由於String字符串的不可變性我們可以十分肯定常量池中一定不存在兩個相同的字符串
----------------------------------------------------------------------//

4. str.equals(str2) --->true


5. String str1="abc";
   String str2="def";
   String str3=str1+str2;
   str3=="abcdef" --->false
   //str3 指向堆中是“abcdef”,但是是在運行時才確定的,== 右邊的“abcdef”是編譯時已經放在常量池的一個值

   1. “== ” 用來比較值是否相等

      1. JAVA 中的8種基本數據類型 浮點型:float(4 byte), double(8 byte) , 整型:byte(1 byte), short(2 byte), int(4 byte) , long(8 byte) , 字符型: char(2 byte) , 布爾型: boolean 變量直接存儲的是“值”

      2. 非基本數據類型時,就是引用類型變量,變量存儲的並不是 “值”本身,而是於其關聯的對象在內存中的地址

    2. equals

       equal 是超類Object 的方法, string對equals 進行了複寫,用於比較指向對象所存儲的內容是否相等

 

12. string 和 char

   C++ : 

   char 是基本數據類型 , 字符;

   string 是封裝好的類,有一些成員函數比如begin()、end();

   char* , 一個指針,可以指向一個字符數組;

   1 . char *s="string" :指向字符常量“string”的指針s,內容是不可以改的 ;

   2 . char s[10]="string":存放字符的數組s , 的內容是可以改的;

   3.  string s = "string" : string 類型的對象;

   4.  char* a[] : 指針類型數組,存放的成員是指針;

13. 內部類

  定義在一個類 或者方法裏面的類:成員內部類、局部內部類、匿名內部類和靜態內部類:

1.成員內部類:

    1  . 創建成員內部類的對象,前提是必須存在一個外部類的對象 ;

    2  外部類.this.成員變量  , 外部類.this。成員方法;

 3. 如果成員內部類用private修飾,則只能在外部類的內部訪問,如果用public修飾,則任何地方都能訪問;如果用protected修飾,則只能在同一個包下或者繼承外部類的情況下訪問;如果是默認訪問權限,則只能在同一個包下訪問。

    4. 匿名內部類:

         
        history_bt.setOnClickListener(new OnClickListener() {
             
            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                 
            }
        }

OnClickListener 就是一個匿名內部類,沒有構造函數,不能有訪問修飾符和static 修飾符。

內部類可以繼承一個與外部類無關的類,保證了內部類的獨立性

14  .java中int char,long各佔多少字節?

類型 位數 字節數
short 2 16
int 4 32
long 8 64
float 4 32
double 8 64
char 2 16

 15  . String,StringBuffer,StringBulilder

    1.string 是字符常量,字符不可變,StringBuffer 和StringBuilder是字符串變量,字符可變

    2.StringBuilder是後出來的,無鎖,是線程不安全的,而StringBuffer有鎖,是線程安全的

 16. 多線程 相關

https://www.wengbi.com/thread_84044_1.html

   1、多線程有什麼用?

    1)發揮多核CPU的優勢 , 2)防止阻塞

   2、創建線程的方式

   1)繼承Thread類  , 2)實現Runnable接口

  3、start()方法和run()方法的區別

    run() 是線程運行時執行的邏輯

    start () 是線程啓動的時間點

   4.Runnable接口和Callable接口的區別

    1)Runnable接口中的run()方法的返回值是void,它做的事情只是純粹地去執行run()方法中的代碼而已;

    2)Callable接口中的call()方法是有返回值的,是一個泛型,和Future、FutureTask配合可以用來獲取異步執行的結果;

    7、什麼是線程安全

    如果你的代碼在多線程下執行和在單線程下執行永遠都能獲得一樣的結果,那麼你的代碼就是線程安全的。

    8、Java中如何獲取到線程dump文件

    死循環、死鎖、阻塞、頁面打開慢等問題,打線程dump是最好的解決問題的途徑。所謂線程dump也就是線程堆棧,獲取到線程堆棧有兩步:

   (1)獲取到線程的pid,可以通過使用jps命令,在Linux環境下還可以使用ps -ef | grep java

   (2)打印線程堆棧,可以通過使用jstack pid命令,在Linux環境下還可以使用kill -3 pid

  9. 多線程共享數據的方式: 

   1. 如果每個線程執行的代碼相同,可以使用同一個Runnable對象,這個Runnable對象中有那個共享數據;

   2. 如果每個線程執行的代碼不同,這時候需要用不同的Runnable對象,即

       1)將共享數據封裝成另外一個對象,然後將這個對象逐一傳遞給各個Runnable對象,每個線程對共享數據的操作方法也分配到那個對象身上完成,這樣容易實現針對數據進行各個操作的互斥和通信;

       2)將Runnable對象作爲一個類的內部類,共享數據作爲這個類的成員變量,每個線程對共享數據的操作方法也封裝在外部類,以便實現對數據的各個操作的同步和互斥,作爲內部類的各個Runnable對象調用外部類的這些方法。                                           3)多個線程對共享數據的操作是不同的,將共享數據和操作共享數據的方法放在同一對象中,將這個對象作爲參數傳遞給Runnable的子類,在子類中用該對象的方法對共享數據進行操作

   10. sleep() 和 wait() 的區別

    1) sleep()屬於Thread 類,程序暫停執行指定的時間,讓出cpu該其他線程,但是他的監控狀態依然保持者,當指定的時間到了又會自動恢復運行狀態。sleep時線程不會釋放對象鎖

    2) wait()方法屬於Object類, 線程會放棄對象鎖,進入等待此對象的等待鎖定池,只有針對此對象調用notify()方法後本線程才進入對象鎖定池準備。

  11. 垃圾回收 

    根據對象存活的時間劃分成3個世代:年輕、年老和永久。當一個對象存活時間足夠長的時候,它就會被複制到年老世代中,對於不同的世代可以使用不同的垃圾回收算法

    調用System.gc() 進行垃圾回收

 

   

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