基礎
Q:Integer和int的區(qū)別
int是基本數據類型,Integer是類,因為Java很多地方需要類,比如泛型里
Java有自動裝箱和自動拆箱
對象類都是final的
-128到127之間,Integer.valueOf返回的是緩存,不新建對象。這個范圍可以通過JVM參數設置
Q:Java和C++的區(qū)別
Java和C++都是面向對象的,都有封裝繼承和多態(tài)
C++有指針,需要程序員手動釋放內存,Java沒有指針,不需要手動釋放內存
C++支持多繼承,Java不支持多繼承
Q:重寫、重載
重載是一個類里,函數名相同,參數不同,返回值和訪問權限不要求
重寫是父類和子類之間,對象方法,static方法不行,是基于對象方法的。父類的方法在子類里必須可見,private不行。要求函數名相同,參數相同,返回值子類小于父類,編譯異常子類小于父類,訪問修飾符,子類大于等于父類。屬性沒有覆蓋的概念,是一種隱藏。
Q:重載的方法,參數個數相同,都是引用類型,調用的時候如果每個參數都傳入null,會有什么反應?編譯報錯?運行報錯?
IDE報錯,編譯不通過
Q:static方法可以被子類重寫嗎
不可以,不能被重寫,只能同名隱藏
父類引用指向子類對象,只會調用父類的靜態(tài)方法,不具有多態(tài)性??梢岳^承
Q:初始化
局部變量(函數內的變量)不初始化會報錯,成員變量有默認的初始值,數組對象是null
Q:instanceof
對象是不是這個類的對象,返回true或false
接口和父類都是true
Q:transient瞬時的
某成員變量有transient,不參與序列化
Q:代碼塊、靜態(tài)代碼塊、構造代碼塊
代碼塊:在方法內部,目的是控制變量生命周期,及早釋放,避免命名沖突
靜態(tài)代碼塊,類里static{},作用是初始化類里的static變量。
在jvm加載類的時候執(zhí)行,只執(zhí)行一次,在主方法之前
構造代碼塊:初始化成員變量,構造方法共同的初始化可以提到構造代碼塊
每次創(chuàng)建對象時調用,在構造方法之前
Q:Object中的方法
1.clone
2.toString
3.hashcode
4.equals
5.getclass
6.wait
7.notify
8.notifyAll
9.finalize
Q:null
null可以強制轉換成任何類,可以用來調用靜態(tài)方法
((People)null)testMethod();
對象方法會報空指針
Q:實參個數可變
public void test(int....i){}
不確定個實參,可以是0個,可以不傳,是一個數組
public void test(int[] i)
會報錯,多重定義
Q:參數傳遞
Java是值傳遞,對象數組是拷貝地址
Q:實例化方法
1.new
2.clone,不調用構造方法
3.反序列化
4.反射newInstance,會調用無參的構造方法,沒有回報錯
5.工廠方法:String str = String.valueOf(23)
Q:String
Java虛擬機中有一個字符串池,是共享的,final的,不用擔心改變
new String()會在堆里分配,而且不會再放到常量池中,要放進常量池需要intern()方法,這個方法放進常量池,返回常量池對象
Q:字符串內存分配
String s1 = new String("abc"); // 內存分配
String s2 = "abc";
if (s1 == s2) {
// 能不能進來? n
}
String s3 = "abc";
if (s2 == s3) {
// 能不能進來? y
}
s2 = new String("abc");
if (s1 == s2) {
// 能不能進來? y
}
s2 = s1;
s1 = "bcd";
// s2 = ? ab
Q:String、StringBuilder、StringBuffer
String不可變
StringBuilder可變,但是不線程安全,性能好
StringBuffer,線程安全
Q:final、finally、finalize
- final修飾變量、類、方法,代表不可變,final static 代表常量
JVM會對方法,變量,類進行性能優(yōu)化
final變量必須在聲明的時候初始化或者在構造方法初始化
匿名類中所有變量都必須是final的- finally
異常中使用,代表一定會執(zhí)行,用來斷開連接,釋放資源等,return會覆蓋try,返回
除非try中exit或者退出虛擬機了- finalize
finalize 方法是Object方法,在對象被回收前調用,作用是釋放c++層的內存等
如果方法是純Java寫不需要寫這個方法,finalize只執(zhí)行一次,可以對象復活一次
垃圾回收的時候,判斷這個對象是否覆蓋了finalize方法,沒有覆蓋直接回收,有finalize方法,且沒有執(zhí)行過就放入F-Queue隊列,由一個線程執(zhí)行該隊列中的finalize方法,執(zhí)行完垃圾回收器再次判斷,是否可達,不可達回收,否則復活
如果在執(zhí)行finalize方法時,出現異常,垃圾回收器不會報異常,只會該對象的finalize執(zhí)行退出
Q:抽象類和接口的區(qū)別
1.接口沒有構造方法,抽象類有構造方法
2.抽象類可以有普通方法,接口所有方法都是抽象的
3.抽象類中有普通成員變量,接口沒有普通成員變量
4.接口方法只能是public,抽象類可以是public、protected、默認類型
5.接口中可以有靜態(tài)方法,但是必須實現
接口中可以包含靜態(tài)成員變量,但是必須是public static final,抽象類都可
Q:多態(tài)
父類的引用指向不同的子類對象,可以不修改代碼,讓引用綁定到不同的類
父類引用可以調用父類中定義的所有屬性和方法,只存在子類的方法和屬性就不能訪問了
內部類
Q:為什么需要內部類
1.通過內部類來隱藏信息
2.實現多重繼承
Q:內部類為什么可以訪問外部類變量
含有一個外部類的this指針
內部類分:成員內部類、局部內部類、靜態(tài)內部類、匿名內部類
內部類可以無條件訪問外部所有屬性和方法,包括private和靜態(tài)成員
和普通的成員一樣
匿名內部類在new后面,只能生成一個對象
編譯的時候由系統(tǒng)自動起名Outter$1.class
沒有類名,不能寫構造方法,編譯器會生成一個不帶參數的構造方法
要初始化可以用構造代碼塊
不能有靜態(tài)方法(沒有類名怎么調用?。?/p>
Q:內部類為什么持有外部對象,static內部類為什么沒有
內部類會生成一個私有構造函數,傳入外部對象
class Outer$Inner{
private Outer&Inner(Outer paramOuter){}
}
這是靜態(tài)內部類編譯后的字節(jié)碼文件,編譯器并沒有為它添加額外的構造函數,所以它其實和我們的外部類沒有任何關系,這是寫在同一個.java源文件中而已.
Q:匿名內部類使用的參數為什么必須是final的
匿名內部類編譯后也是單獨一個class文件,僅僅有一個外部類的引用,外部傳入參數,其實是參數的拷貝,內部的修改并不影響外部。這樣從程序員角度看是同一個,內部改變了外部確沒變,為了保持一致,所以規(guī)定用final避免形參可變。
內部類,實際會在匿名匿名內部類的構造方法中拷貝一份要使用的外部變量。這樣,就牽扯兩份變量的同步問題,比如內部類實例被回收、匿名內部類被實例化之前外部變量被更改等問題。解決該問題的方法,就是強制使用final,保證引用不可變
Q:內部類為什么可以訪問外部private變量
Q:靜態(tài)內部類和非靜態(tài)的區(qū)別
靜態(tài)內部類和靜態(tài)成員變量類似
不能使用外部非靜態(tài)成員變量或方法
普通內部類都可以方法
靜態(tài)內部類可以聲明普通成員變量和方法,普通內部類不能聲明static成員變量和方法
靜態(tài)內部類可以單獨初始化,不依賴于外部
Inner i = new Outer.Inner()
//普通內部類初始化
Outer o = new Outer();
Inner i = o.new Inner();
Q:枚舉
枚舉也是類,都繼承了Enum,每一個變量是類的對象,還是static final修飾的
構造方法是private,沒有辦法創(chuàng)建枚舉值??梢杂谐蓡T變量,成員方法
public enum Color{ RED,BLUE,BLACK }
編譯后
final enum Color{
public static final Color RED;
public static final Color BLUE;
//靜態(tài)內部類,對對象初始化
static{
new Color[1]
}
private Color(){}
}
繼承Enum方法
(1)ordinal():返回枚舉的順序
Color.RED.ordinal(); 返回0
(2)compareTo():比較兩個枚舉的順序
(3)values()返回全部枚舉的數組
Color[] colors = Color.values();
(4)toString()返回常量的名
Color c = Color.RED;
System.out.println(c); 返回RED
(5)valueOf,返回這個名詞的常量
Color.valueOf(“BLUE”); 返回Color.BLUE
Q:IO
字節(jié)流InputStream/OutputStream
字符流 Reader/Writer
字符流是字節(jié)流讀取時查了指定的碼表
字節(jié)流以字節(jié)(8bit)為單位,字符流以字符為單位
字節(jié)流能處理所有類型的數據,如圖片、AVI,字符流只能處理字符數據
優(yōu)先使用字節(jié)流
反射
Q:什么是反射
反射機制指的是程序在運行時能夠獲取自身的信息
在Java中,給定類的名字,那么就可以通過反射機制來獲得類的所有信息。
Class.forName("com.mysql.jdbc.Driver.class").newInstance();
hibernate、struts都是用反射機制實現的。(android藍牙)
Q:為什么要有反射,反射是怎么解決這個問題的,Android里如何用的項目里你是如何用反射的
反射可以在運行的時候動態(tài)的加載,而不是固定的,增加了靈活性
比如實例化一個對象new Person(),如果想實例化其他的對象,不想修改源碼,就可以用反射class.forName(“person”).newInstance(),把加載哪個類寫到配置文件中
Q:三種獲取Class對象方法
1.Class的靜態(tài)方法 Class.forName(“java.lang.String”);
2.使用類的.class語法 String.class;
3.對象的getClass()方法
String str = “aa”;
Class<?> classType = str.getClass();
Q:反射創(chuàng)建對象,獲取構造器
不帶參的構造方法生成對象有兩種方式
1.newInstance()直接生成即可,調用無參構造方法
Class<?> clazz = String.class;
Object obj = clazz.newInstance();
2.用Constructor的構造方法調用newInstance(參數)
Class<?> clazz = Customer.class;
//獲得無參構造方法
Constructor cons = clazz.getConstructor(new Class[]{});
//通過構造方法來生成對象,構造方法調 con.newInstance()
Object obj = cons.newInstance(new OBject[]{}); //傳入參數
如果要通過帶參的構造方法生成對象,只有一種方式
Class<?> clazz = Customer.class;
Constructor cons2 = clazz.getConstructor(new Class[]{String.class,int.class});
Object obj2 = cons2.newInstance(new Object[]{“zhangsan”,20});
私有構造方法加一個cons2.setAccessible(true);
Teacher clazz =Teacher.class;
Constrctor cons = clazz.getConstructor(String.class);
Teacher t = (Teacher)cons.newInstance(“aa”);
Q:反射獲取私有屬性并修改
1.獲得CLass
2.創(chuàng)建對象
3.getField或getDeclaredField獲取Field
4.修改
Class<?> clazz = Class.forName("com.ang.Teacher");
通過有參構造獲取對象,有參構造必須為public類型
Constructor c = clazz.getConstructor(String.class,int.class);//字節(jié)碼階段,構造方法的參數只能是字節(jié)碼對象;
Teacher t = (Teacher) c.newInstance("王艷",100); //創(chuàng)建實例對象
Field field = clazz.getDeclaredField("name");//獲取私有成員變量
field.setAccessible(true);//去除私有權限
field.set(t,”李代理”); //傳入對象和這個屬性的新值
Q:反射如何調用私有方法
Class<?> clazz = Class.forName("com.ang.Teacher");
Constructor c = clazz.getDeclaredConstructor(); //獲取私有無參構造
c.setAccessible(true); //去除私有權限
Teacher t = (Teacher) c.newInstance();//通過私有構造獲取實例
Method method = clazz.getDeclaredMethod("add", int.class,int.class);//字節(jié)碼階段反射有參方法是,需傳入參數類型的字節(jié)碼
method.setAccessible(true); //去除私有權限
int num = (Integer) method.invoke(t, 20,8); //invoke第一個是類的實例,第二個是參數
如果是靜態(tài)方法,第一個參數,傳入null
method.invoke(null,20,8);
getMethods()獲得該類所有公有的方法,getDeclaredMethod(parameterTypes)獲得該類某個方法,getDeclaredMethods()獲得該類所有方法。帶有Declared修飾的方法可以反射到私有的方法,沒有Declared修飾的只能用來反射公有的方法。其他的Annotation、Field、Constructor也是如此
Q:final成員變量修改
Java反射可以修改final成員變量嗎?
分別兩種情況
1.在定義的時候就初始化了值,private final String name = “huang”;因為編譯器final類型的數據自動被優(yōu)化了,所有用到的地方都替換成立常量,所以是return “huang”,而不是return this.name。所以不能修改,向阻止編譯自動優(yōu)化,可以改為final String name = (null!=null?”ddd”:”huang”)
2.如果是在構造函數初始化,可以修改
final Class<?> clz = p.getClass();
final Field nameField = clz.getDeclaredField("name");
nameField.setAccessible(true);
nameField.set(p, String.valueOf("huang.damon"));
equals、hashcode
Q:equals、hashcode
為了提高效率,先判斷hashcode,hashcode不相同,equals肯定不相同,就不用算了
如果hashcode相同,再判斷equals。這樣實際調用equals的次數就大大減少了
在HashSet中的使用,set不允許有相同的對象,就先hashcode,在equals,相同就不存
如果你的類不放在hashcode為基礎的容器就不需要重新hashcode
但是如果要放到hashset或者hashmap,他們equals相同,hashcode必須相等
result = 29*getName().hashCode()+getBirthday.hashCode();
Q:怎么重寫hashcode方法,String的hashcode方法
用equals中涉及到的屬性來計算,equal相同的hashcode要相同
首先定義個初始值,一般來說取17
然后根據屬性類型解析
1.boolean: hashcode = a?1:0
2.int,byte,shot,char : hashcode = (int)b
3.long: hashcode = c^c>>>32
4.float: hashcode = d.hashCode()
5.double: hashcode = e.hashCode()
6.引用:若為null則為0,否則遞歸調用引用的hashcode
7.數組,String:s[0]31 ^ (n-1) + s[1] * 31 ^ (n-2) + ..... + s[n-1]
第三步
result = result31 +hashcode
Q:自定義重寫equals的同時還需要重寫哪個方法
一般應該重寫equals()還有hashCode()
Q:如果沒有重寫hashcode會發(fā)生什么
有些集合是不允許重復元素出現的,這兩個方法用來保證集合中沒有重復元素
如果只重寫了equals方法,兩個對象equals返回了true,但是沒有重寫hashcode方法
集合還是會插入元素,這樣集合中就出現重復元素了
hashMap的put方法是先調用hashCode定位到數組的位置,如果該數組的位置上已經存在元素了,即table[i]!=null,那么遍歷鏈表,調用equals方法判斷key是否相等,如果equal不相同,說明沒有找到這個key,表明這個key不存在,則會插入
如果沒有重寫hashcode方法,那么就無法定位到同一個數組位置,集合還是會插入元素,這樣集合中就出現重復元素了,重寫equals就沒有意義了
如果重寫了hashcode就能定位到數組相同的位置,就可以遍歷這條單向鏈表,用equals判斷這兩個對象是否相同,如果相同就覆蓋,不相同,就插入到鏈表的頭節(jié)點處
Q:為什么要有拷貝?解決了什么問題,怎么解決的,什么場景使用
從一個已有的對象,創(chuàng)建一個相似或相同的對象,是模板設計模式,簡化對象的創(chuàng)建
值直接拷貝,引用也直接拷貝,不行,怎么辦?引用也實現拷貝,遍歷值拷貝
或者先把對象序列化,再反序列化,是另一個對象了
淺拷貝:值直接拷貝,引用和對象,拷貝的地址,指向同一塊內存,沒有新開辟地址
深拷貝:引用和對象新開辟地址,也全部拷貝
clone方法是淺拷貝,implements Cloneable
要實現深拷貝
1.類里的每一個對象都重新clone方法,在頂層類,調用所有對象的clone方法
age是一個成員變量,age里實現clone方法,Student里有age,再調用age的clone方法
2.通過對象的序列化實現深拷貝
先把這個對象序列化,再反序列化給另一個對象
oos.writeObject(stu1);
Student stu2 = (Student)ois.readObject():
clone()默認是深拷貝還是淺拷貝,如何讓clone實現深拷貝