生活随笔
收集整理的這篇文章主要介紹了
Java总结及面试题
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
待整合:Java綜合中級面試題
1.體系結構
2.Java編程語言
Java教程
面試題
數據類型 char 型變量中能不能存貯一個中文漢字,為什么? 答:char類型可以存儲一個中文漢字,因為Java中使用的編碼是Unicode(不選擇任何特定的編碼,直接使用字符在字符集中的編號,這是統一的唯一方法),一個char類型占2個字節 (16比特),所以放一個中文是沒問題的。Java 語言中一個字符占幾個字節? 如果沒有指定編碼,采用Unicode,一定占用2個字節。Java 語言中一個字符占幾個字節? - 知乎 字符轉字節byte char 轉 byte[] 數組
public static byte[] charToByte(char c) {byte[] b = new byte[2];b[0] = (byte) ((c & 0xFF00) >> 8);b[1] = (byte) (c & 0xFF);return b;}byte[] 數組轉 char
public static char byteToChar(byte[] b) {char c = (char) (((b[0] & 0xFF) << 8) | (b[1] & 0xFF));return c;}char[] 數組轉為byte[] 數組
public static byte[] getBytes(char[] chars) {Charset cs = Charset.forName("UTF-8");CharBuffer cb = CharBuffer.allocate(chars.length);cb.put(chars);cb.flip();ByteBuffer bb = cs.encode(cb);return bb.array();}byte[] 數組轉為數組 char[]
public static char[] getChars(byte[] bytes) {Charset cs = Charset.forName("UTF-8");ByteBuffer bb = ByteBuffer.allocate(bytes.length);bb.put(bytes);bb.flip();CharBuffer cb = cs.decode(bb);return cb.array();} float f=3.4;是否正確? 答:不正確。3.4是雙精度數,將雙精度型(double)賦值給浮點型(float)屬于下轉型(down-casting,也稱為窄化)會造成精度損失,因此需要強制類型轉換float f =(float)3.4; 或者寫成float f =3.4F。short s1 = 1; s1 = s1 + 1;有錯嗎?short s1 = 1; s1 += 1;有錯嗎? 答:不能正確編譯,對于short s1 = 1; s1 = s1 + 1;由于1是int類型,因此s1+1運算結果也是int 型,需要強制轉換類型才能賦值給short型。 而short s1 = 1; s1 += 1;可以正確編譯,因為s1+= 1;相當于s1 = (short)(s1 + 1);其中有隱含的強制類型轉換。數據類型之間的轉換: 如何將字符串轉換為Integer對象?如何將int數據類型轉換為字符串? ?調用Integer對象方法parseXXX(String)或valueOf(String)即可返回相應基本類型; 例:字符串轉為數字?
String s="123456";
Integer i1=Integer.parseInt(s);
Integer i2=Integer.valueOf(s);
System.out.println(i1+i2); 一種方法是將基本數據類型與空字符串("")連接(+)即可獲得其所對應的字符串;另一種方法是調用String 類中的valueOf()方法返回相應字符串 int a=1;
int b=2;
int c=3;
String s1=""+a+b+c;
String s2=String.valueOf(a)+String.valueOf(b)+String.valueOf(c);
System.out.println(s1+s2); 查看下列代碼的輸出結果: 1. float a=0.125f;double b=0.125d;System.out.println((a-b)==0.0);//A.考察基本數據類型轉換,在此題中float的a會向上轉為double,不會丟失精度,而且浮點數計算之后也是浮點數,所以是0.0,沒有問題A.true B.flase2. double a=0.8;double b=0.7;double c=0.6;System.out.println((a-b)==(b-c));//false,System.out.println(a-b);//0.10000000000000009System.out.println(b-c);//0.09999999999999998//主要考察十進制小數計算丟失精度。double類型會先轉為2進制,進行計算后因為無法用2進制數準確表示而導致精度丟失產生近似值,//所以結果不相等
Java 中 int 和 Integer 的區別 int 是基本數據類型,int 變量存儲的是數值。Integer 是引用類型,實際是一個對象,Integer 存儲的是引用對象的地址。 int 和 Integer 所占內存比較: Integer 對象會占用更多的內存。Integer 是一個對象,需要存儲對象的元數據。但是 int 是一個原始類型的數據,所以占用的空間更少。 兩個非 new 生成的 Integer 對象進行比較,如果兩個變量的值在區間?[-128,127] ?之間,比較結果為 true;否則,結果為 false。 Integer i1 = 127;
Integer ji = 127;
System.out.println(i1 == ji);//輸出:true
Integer i2 = 128;
Integer j2 = 128;
System.out.println(i2 == j2);//輸出:false new 生成的是兩個對象,其內存地址不同。 Integer i = new Integer(100);
Integer j = new Integer(100);
System.out.print(i == j); //false 非 new 生成的 Integer 變量與?new Integer()?生成的變量比較,結果為 false。 Integer i= new Integer(200);
Integer j = 200;
System.out.print(i == j);
//輸出:false ?Integer 變量(無論是否是 new 生成的)與 int 變量比較,只要兩個變量的值是相等的,結果都為 true。 Integer i1 = 200;
Integer i2 = new Integer(200);
int j = 200;
System.out.println(i1 == j);//輸出:true
System.out.println(i2 == j);//輸出:true 變量 實例變量和靜態變量 實例變量必須依存于某一實例,需要先創建對象然后通過對象才能訪問到它。 靜態變量是被static修飾符修飾的變量,也稱為類變量,它屬于類,不屬于類的任何一個對象,一個類不管創建多少個對象,靜態變量在內存中有且僅有一個拷貝,靜態變量可以實現讓多個對象共享內存; 用private修飾static變量的意圖:可以參考單例設計模式final變量 finla變量的數值在初始化之后不能再進行改變。final變量必須在聲明的時候初始化。 運算符 &和&&的區別? &&是邏輯運算符,經常用在表達式中 ,以構造更復雜的判斷表達式。 &是邏輯位運算符。位運算符是對操作數以二進制為單位 進行的操作和運算。 用最有效率的方法計算2乘以8? 答: 使用移位運算符。2 << 3(左移3位相當于乘以2的3次方,右移3位相當于除以2的3次方)。~非運算符 System.out.println(~4); // -5// 00000100 4的原碼// 11111011 異或,是一個負數,負數在計算機中是以補碼的儲存的// 方法1:補碼減1取反就是原碼// 11111010 補碼-1就是反碼// 10000101 反碼取反就是原碼,-5// 方法2:補碼的補碼就是原碼,也就是補碼取反加1就是原碼// 10000100 取反// 10000101 +1// -5 求補碼,也就是在求在計算機存儲的二進制// 10000101// 11111010// 11111011 移位運算符 已知補碼如何求原碼:補碼的補碼就是原碼,就像負負得正一樣
如-4
原碼10000100
反碼11111011
補碼11111100
反碼10000011
補碼10000100
得-4< 比較大小
<< 有符號左移動
<<<(不存在此種符號)
> 比較大小
>> 有符號右移
>>>是java中的移位運算符,表示無符號右移-20 >> 2 結果是-5,-20 >> 2是有符號右移,高位補1,而-20的補碼是11101100 ,右移兩位高位補1:結果是11111011,也就是-5
-20
原碼 10010100
反碼 11101011(在原碼的基礎上符號位不變,其它位取反)
補碼 11101100(在反碼的基礎上+1)
右移兩位(高位補1)結果為11111011 結果為-5
注意由于高位補1,因此-1右移的結果總是–1
例:System.out.println(-5 >> 3); // -1-5原碼 10000101反碼 11111010補碼 11111011右移3位,高位補1為:11111111補碼的補碼為原碼: 10000001=-1-20 >>> 2結果是1073741819
-20由于是int類型
原碼為 10000000000000000000000000010100
反碼為 11111111111111111111111111101011
補碼為 11111111111111111111111111101100
右移兩位00111111111111111111111111111011
轉為10進制為1073741819(可以用在線二進制轉十進制轉換)驗證:
System.out.println(Integer.toBinaryString(-20)); //11111111111111111111111111101100
System.out.println(-20>>>2); // 1073741819
以下的結果是什么 int i=0;
int k=++i + i++ + ++i + i;
System.out.println(k);//8
修飾符 Java 中的final關鍵字有哪些用法?
修飾類:表示該類不能被繼承;哪些類已經使用final修飾?設計目的? 修飾方法:表示方法不能被重寫; 修飾變量:表示變量只能一次賦值以后值不能被修改(常量)。 final
final可以修飾類、成員變量、方法1.修飾類:
安全:在被使用的過程中,對象不會發生變化
當一個類被定義為final時,它的所有方法都自動成為final方法,但不影響對變量的定義。
其方法不想被重寫,所有用final修飾,那就不能被繼承了,方法也就不能被重寫了,比如LocalDateTime.now()方法,如果不用final修飾,你給我一重寫,獲取到的時間不對勁了
java中被final修飾的常用類有哪些?
以下是個人總結出常用,常見的:
在五個不同的包中:
java.lang包中
包裝類:Boolean,Character,Short,Integer,Long,Float,Double,Byte,Void(八大類型的包裝類型加一個void)(*)
字符串類:String,StringBuilder,StringBuffer(*)
系統類:Math,StrictMath(*)
其他:Character,UnicodeBlock,ProcessBuilder,StackTraceElement
java.util包
UUID,Scanner,Optional
.java.lang.reflect包
Array,Constructor,Field,Parameter,ReflectPermission(*)
java.net包
HttpCookie,URL,URI(*)
java.time包
除了Clock,ZoneId之外都是,如LocalDateTime、LocalDate、Year,YearMonth,MonthDay等(*)
注意:其中標紅(結尾帶*)的是最常見的.1.final修飾類的中的方法不用final修飾不用final修飾類中有哪些方法使用final修飾的?HashMap.class中有一些這樣的方法2.修飾變量:表示變量只能一次賦值以后值不能被修改(常量)。
哪些包,哪些類中常用,特點
java.lang包中基本都有幾個public static final修飾的變量Math類用了final修飾,其中大量的變量用了final、static修飾,都是比較常用的,如PI=3.14156
java.time包中的LocalDateTime、LocalDate、ZoneId、Year有public static final修飾的變量
這些用public static final修飾的變量,主要在lang包、time中,且是數字、時間等不可變對象中眾所周知的常量
大多數是private static final修飾的變量,僅供本類使用
所以項目中的常量推薦中default修飾
本類常量用private修飾3.修飾方法:表示方法不能被重寫;
猜測:final修飾的方法用private、default
用default修飾,暫未見到private修飾的,時刻多注意4.修飾內部類 Java 中的static關鍵字有哪些用法? 修飾變量:當定義一個static的變量的時候jvm會將將其分配在內存堆上,所有程序對它的引用都會指向這一個地址而不會重新分配內存; static修飾屬性:無論一個類生成了多少個對象,所有這個對象共用唯一一份靜態的成員變量;一個對象對該靜態成員變量進行了修改,其他對象的該靜態成員變量的值也會隨之發生變化 。 如果一個成員變量是static的,那么我們可以通過類名.成員變量名的方式來使用它(推薦使用這種方式)。 修飾程序塊:當修飾一個程序塊的時候(也就是直接將代碼寫在static{...}中)時候,虛擬機就會優先加載靜態塊中代碼,這主要用于系統初始化; 修飾類方法:當修飾一個類方法時候你就可以直接通過類來調用而不需要新建對象。 static修飾方法:static修飾的方法叫做靜態方法。對于靜態方法來說,可以使用類名.方法名的方式來訪問。 靜態方法只能繼承,不能重寫(Override)。 修飾內部類 Base64 {public static class Encoder {}
} static:
類加載器在加載這個類的時候就已經實例化了這個類。
用static修飾的方法可以用類名直接調用,不用的一定要先實例化一個對象然后才可以調用。
如果你需要通過計算來初始化你的static變量,你可以聲明一個static塊,Static 塊僅在該類被加載時執行一次
1.當一個方法或者變量需要初始化加載,或者是經常被調用的時候可以加上static。
壞處:
1.初始化加載,比較占內存,所以不經常用的方法,不建議加此關鍵字。
2.如果static是寫在單例中,高并發訪問是會出問題的,這時候就要設置線程等待了,static是在容器加載的時候就已經加載到內存中,所以static方法和變量不宜過度使用,有選擇的使用。
還可以修飾成員內部類
Base64 {public static class Encoder {}
}
有時你希望定義一個類成員,使它的使用完全獨立于該類的任何對象。通常情況下,類成員必須通過它的類的對象訪問,但是可以創建這樣一個成員,它能夠被它自己使用,而不必引用特定的實例。在成員的聲明前面加上關鍵字static(靜態的)就能創建這樣的成員。如果一個成員被聲明為static,它就能夠在它的類的任何對象創建之前被訪問,而不必引用任何對象。你可以將方法和變量都聲明為static。static成員的最常見的例子是main( ) 。因為在程序開始執行時必須調用main() ,所以它被聲明為static。
聲明為static的變量實質上就是全局變量。當聲明一個對象時,并不產生static變量的拷貝,而是該類所有的實例變量共用同一個static變量。聲明為static的方法有以下幾條限制:
·它們僅能調用其他的static 方法。
·它們只能訪問static數據。
·它們不能以任何方式引用this 或super(關鍵字super 與繼承有關,在下一章中描述)。
如果你需要通過計算來初始化你的static變量,你可以聲明一個static塊,Static 塊僅在該類被加載時執行一次。下面的例子顯示的類有一個static方法,一些static變量,以及一個static 初始化塊:
// Demonstrate static variables,methods,and blocks.
class UseStatic {static int a = 3; static int b; static void meth(int x) { System.out.println("x = " + x);System.out.println("a = " + a);System.out.println("b = " + b);}static { System.out.println("Static block initialized.");b = a * 4; } public static void main(String args[]) { meth(42);}
}
一旦UseStatic 類被裝載,所有的static語句被運行。首先,a被設置為3,接著static 塊執行(打印一條消息),最后,b被初始化為a*4 或12。然后調用main(),main() 調用meth() ,把值42傳遞給x。3個println ( ) 語句引用兩個static變量a和b,以及局部變量x 。
注意:在一個static 方法中引用任何實例變量都是非法的。
下面是該程序的輸出:
Static block initialized.
x = 42
a = 3
b = 12
在定義它們的類的外面,static 方法和變量能獨立于任何對象而被使用。這樣,你只要在類的名字后面加點號運算符即可。例如,如果你希望從類外面調用一個static方法,你可以使用下面通用的格式:
classname.method( )
這里,classname 是類的名字,在該類中定義static方法??梢钥吹?#xff0c;這種格式與通過對象引用變量調用非static方法的格式類似。一個static變量可以以同樣的格式來訪問——類名加點號運算符。這就是Java 如何實現全局功能和全局變量的一個控制版本。
下面是一個例子。在main() 中,static方法callme() 和static 變量b在它們的類之外被訪問。class StaticDemo {static int a = 42; static int b = 99;static void callme() { System.out.println("a = " + a);}
} class StaticByName {public static void main(String args[]) { StaticDemo.callme();System.out.println("b = " + StaticDemo.b);}
}
下面是該程序的輸出:
a = 42
b = 99注意:如果static是寫在單例中,高并發訪問是會出問題的,這時候就要設置線程等待了,static是在容器加載的時候就已經加載到內存中,所以static方法和變量不宜過度使用,有選擇的使用,類成員的使用
好處:對對象的共享數據進行單獨空間的存儲,節省空間,沒有必要每一個對象中都存儲一份,可以直接被類名調用。
弊端:生命周期過長。
局部變量初始化
局部變量定義后,必須顯式初始化后才能使用,因為JVM不會為局部變量執行初始化操作。這就意味著,定義局部變量后,JVM并未為這個變量分配內存空間。直到程序為這個變量賦值時,系統才會為局部變量分配內存,并將初始值保存到該內存中。
局部變量不屬于任何類或實例,因此它是保存在其所在方法的棧幀內存中。
基本數據局部變量:基本數據類型變量的值會直接保存到該變量所對應的內存中。
引用數據局部變量:變量內存中存的是堆中對象的地址,通過該地址引用到該變量實際指向的堆里的對象。
棧幀內存中的變量隨方法或代碼塊的運行結束而銷毀,無須JVM回收。一點小建議開發中應該盡量縮小變量的作用范圍,如此在內存中停留時間越短,性能也就更高。
合理使用static修飾,一般只有定義工具方法的時候使用;
static方法需要訪問的變量,只有該變量確實屬于類,才使用static修飾字段;
盡量使用局部變量;jdk1.8中哪些地方用到了static
首先聲明static和final沒有特別緊密的聯系,也就是說有些地方static和final一起使用,有些地方static單獨使用接口中聲明全局常量
interface SocksConsts{static final int PROTO_VERS4 = 4;
} 大多數時候看到static總是和final在一起配合使用,那這兩者一起使用的場景是哪些? public,為什么使用public? default,為什么使用default?,只有同一個包下的類才能訪問 private,為什么使用private? ? 字符串 String 屬于基礎的數據類型嗎? String 不屬于基礎類型,基礎類型有 8 種:byte、boolean、char、short、int、float、long、double,而 String 屬于對象。
String的最大長度 1:“字節”是byte,“位”是bit ; 2: 1 byte = 8 bit ; char 在java中是2個字節。java采用unicode,2個字節(16位)來表示一個字符。 String底層是char數組, 最大長度就取決于數組長度. 數組長度為int類型,數組的空間會有兩個元素空間不能用,?實際最大可分配長度為Integer.MAX_VALUE-2 , 2^31-?2, String最大長度就是這個值. 一個char占2字節, 占用內存約4G
是否可以繼承String類? 答:String 類是final類,不可以被繼承。
在java中String類為什么要設計成final? 1. 什么是不可變? 2. String為什么不可變? 3. 不可變有什么好處? 在java中String類為什么要設計成final? - 知乎
String 類的常用方法都有那些? Java String 類 | 菜鳥教程
substring() :一個參數代表始于指定索引處的字符,一直到此字符串索引末尾,兩個參數代表該子字符串從指定的 beginIndex 處開始, endIndex:到指定的 endIndex-1處結束。indexOf():返回指定字符的索引。 charAt():返回指定索引處的字符。 replace():字符串替換。 replaceAll():字符串替換。 trim():去除字符串兩端空白。 split():分割字符串,返回一個分割后的字符串數組。 getBytes():返回字符串的 byte 類型數組。 length():返回字符串長度。 toLowerCase():將字符串轉成小寫字母。 toUpperCase():將字符串轉成大寫字符。 substring():截取字符串。 equals():字符串比較。 concat():字符串拼接 字符串拼接的方法有哪些? jdk8以前:字符串拼接的5種方法 +(底層實現原理)_覓補的博客-CSDN博客_字符串拼接 使用+拼接字符串 使用String的方法concat 使用StringBuilder的append 使用StringBuffer的append 結論: 直接使用StringBuilder的方式是效率最高的 。因為StringBuilder天生就是設計來定義可變字符串和字符串的變化操作的。如果不是在循環體中進行字符串拼接的話,直接使用+就好了。 如果在并發場景中進行字符串拼接的話,要使用StringBuffer來代替StringBuilder。 jdk8:Java8新特性 之字符串連接_const_-CSDN博客_java8 字符串拼接 StringJoiner方式:可以指定分隔符 String.join:可以指定分隔符 JDK1.8 為 String 類添加了一個新的靜態方法String.join。 Stream方式 分割字符串 split(".")無效 需使用split("\\.")來分割 在正則表達式中,小數點指的是任意字符,因此不能直接用"."來匹配小數點,需要使用"\\." 來匹配小數點,因此要根據小數點分割字符串,需要下面這種寫法。 String name = "cn.yiguang.testProject.annotationScan.pojo.School";
System.out.println(name.split("\\.").length); // 6 如何將字符串反轉? 使用 StringBuilder 或者 stringBuffer 的 reverse() 方法。String中刪除空格 的7種方法 trim() : 刪除字符串開頭和結尾的空格。其實,trim移除的空白字符指的是指ASCII值小于或等于32的任何字符(' U+0020 ') :其中包含了空格、換行、退格等字符。 strip() : 刪除字符串開頭和結尾的空格。 stripLeading() : 只刪除字符串開頭的空格 stripTrailing() : 只刪除字符串的結尾的空格 replace() : 用新字符替換所有目標字符 replaceAll() : 將所有匹配的字符替換為新字符。此方法將正則表達式作為輸入,以標識需要替換的目標子字符串 replaceFirst() : 僅將目標子字符串的第一次出現的字符替換為新的字符串 Java 中操作字符串都有哪些類?它們之間有什么區別? 操作字符串的類有:String、StringBuffer、StringBuilder。 String 和 StringBuffer、StringBuilder 的區別在于 String 聲明的是不可變的對象 ,每次操作都會生成新的 String 對象,然后將指針指向 新的 String 對象,而 StringBuffer、StringBuilder 可以在原有對象的基礎上進行操作,所以在經常改變字符串內容的情況下最好不要使用 String。 StringBuffer 和 StringBuilder 最大的區別在于,StringBuffer 是線程安全 的,而 StringBuilder 是非線程安全的,但 StringBuilder 的性能卻高于 StringBuffer,所以在單線程環境下推薦使用 StringBuilder,多線程環境下推薦使用 StringBuffer。創建String對象需要明白: 第一種方式:直接使用字面量。創建一個字符串時,首先會檢查池中是否有值相同的字符串對象,如果有就直接返回引用,不會創建字符串對象;如果沒有則新建字符串對象,返回對象引用,并且將新創建的對象放入池中。 第二種方式:通過new方法。通過new方法創建的String對象是不檢查字符串常量池的,而是直接在堆中創建新對象,也不會把對象放入池中。 //第一種方式String s1="aa";String s2="bb";String s3="aabb";//="aa"+"bb"//第二種方式String s4=new String("bb");String s5=new String("aabb");String s6=s1+s2;//!="aa"+"bb"String s7=s1+s4;System.out.println(s3==s6);//falseSystem.out.println(s3==s7);//falseSystem.out.println(s3==s5);//falseSystem.out.println(s6==s5);//false String s = new String("xyz");創建了幾個字符串對象? 答:兩個對象,一個是方法區(常量池)的"xyz",一個是用new創建在堆上的對象。請說出下面程序的輸出 class StringEqualTest {public static void main(String[] args) {String s1 = "Programming";String s2 = new String("Programming");String s3 = "Program" + "ming";System.out.println(s1 == s2);//s1指向方法區,s2指向堆 比較的是地址,falseSystem.out.println(s1.equals(s2));//比較的是字符串,trueSystem.out.println(s1 == s3);//s1指向方法區,s3指向方法區,比較的是地址,trueSystem.out.println(s1.equals(s2));//trueSystem.out.println(s2==s3);//falseSystem.out.println(s2.equals(s3));//true}
} 數組有沒有length()方法?String有沒有length()方法? 答:數組沒有length()方法,有length 的屬性。String 有length()方法。空字符串與null的區別 類型 null表示的是一個對象的值,而并不是一個字符串。例如聲明一個對象的引用,String a = null ; ""表示的是一個空字符串,也就是說它的長度為0。例如聲明一個字符串String str = "" ; 內存分配 String str = null ; 表示聲明一個字符串對象的引用,但指向為null,也就是說還沒有指向任何的內存空間; String str = "";??? 表示聲明一個字符串類型的引用,其值為""空字符串,這個str引用指向的是空字符串的內存空間 ;空字符串的內存空間在方法區的常量池中 例子 public class String_Demo01 {public static void main(String[] args) { String str1 = new String() ;String str2 = null ;String str3 = "" ;System.out.println(str1==str2); //內存地址的比較,返回falseSystem.out.println(str1.equals(str2)); //值的比較,返回falseSystem.out.println(str2==str3); //內存地址的比較,返回falseSystem.out.println(str3.equals(str2)); //值的比較,返回falseSystem.out.println(str1==str3); //內存地址的比較,返回falseSystem.out.println(str1.equals(str3)); //值的比較,返回true}} 通過如上的程序可以得出如下結論:
字符串對象與null的值不相等,且內存地址也不相等;
空字符串對象與null的值不相等,且內存地址也不相等;
new String()創建一個字符串對象的默認值為"" (String類型成員變量的初始值為null)
以下是java 判斷字符串是否為空的四種方法: 方法一: 最多人使用的一個方法, 直觀, 方便, 但效率很低:if(s == null || s.equals(""));
方法二: 比較字符串長度, 效率高, 是我知道的最好一個方法:if(s == null || s.length() == 0);
方法三: Java SE 6.0 才開始提供的方法, 效率和方法二幾乎相等, 但出于兼容性考慮, 推薦使用方法二.if(s == null || s.isEmpty());方法四: 這是一種比較直觀,簡便的方法,而且效率也非常的高,與方法二、三的效率差不多:if (s == null || s == "");注意:s == null 是有必要存在的.如果 String 類型為null, 而去進行 equals(String) 或 length() 等操作會拋出java.lang.NullPointerException.并且s==null 的順序必須出現在前面,不然同樣會拋出java.lang.NullPointerException.如下Java代碼:String str = null;if(str.equals("") || str == null){//會拋出異常System.out.println("success");}
number和Math類 Java獲取隨機數的方式 初級 java.lang.Math.random() ?生成一個雙精度浮點數:大于等于 0.0 且小于 1.0 的偽隨機 要產生0.0和10.0之間的雙精度浮點數會這樣來寫:Math.random()?*?10? 產生0和10之間的整數,則會寫成:Math.round(Math.random()*10) 進階 new java.util.Random().nextInt(10) :從0-9中生成一個int類型的整數。(10可以變為100,1000....) 通過閱讀Math.random()的源碼,或者干脆利用IDE的自動完成功能,開發人員可以很容易發現,java.lang.Math.random()使用一個內部的隨機生成對象 - 一個很強大的對象可以靈活的隨機產生:布爾值、所有數字類型,甚至是高斯分布。例如:new java.util.Random().nextInt(10) 它有一個缺點,就是它是一個對象。它的方法必須是通過一個實例來調用,這意味著必須先調用它的構造函數。如果在內存充足的情況下,像上面的表達式是可以接受的;但內存不足時,就會帶來問題。 一個簡單的解決方案,可以避免每次需要生成一個隨機數時創建一個新實例,那就是使用一個靜態類。猜你可能想到了java.lang.Math,很好,我們就是改良java.lang.Math的初始化。雖然這個工程量低,但你也要做一些簡單的單元測試來確保其不會出錯。 并發 在Java EE多線程應用程序的環境中,隨機生成實例對象仍然可以被存儲在類或其他實現類,作為一個靜態屬性。幸運的是,java.util.Random是線程安全的,所以不存在多個線程調用會破壞種子(seed)的風險。 另一個值得考慮的是多線程java.lang.ThreadLocal的實例。偷懶的做法是通過Java本身API實現單一實例,當然你也可以確保每一個線程都有自己的一個實例對象。 雖然Java沒有提供一個很好的方法來管理java.util.Random的單一實例。但是,期待已久的Java 7提供了一種新的方式來產生隨機數:java.util.concurrent.ThreadLocalRandom.current().nextInt(10) :從0-9中生成一個int類型的整數。(10可以變為100,1000....)int和Integer有什么區別? int的包裝類就是Integer。Math.round(11.5) 等于多少?Math.round(-11.5)等于多少? 答:Math.round(11.5)的返回值是12,Math.round(-11.5)的返回值是-11。四舍五入的原理是在參數上加0.5然后進行下取整。寫出運行結果 public?static?void?main(String[]?args)?{??Integer?a?=?new?Integer(3);??Integer?b?=?3;??????????????????//?將3自動裝箱成Integer類型??int?c?=?3;??System.out.println(a?==?b);?????//?false?兩個引用沒有引用同一對象??System.out.println(a?==?c);?????//?true?a自動拆箱成int類型再和c比較??}??
}?? 寫出運行結果 public class Test03 {public static void main(String[] args) {Integer f1 = 100, f2 = 100, f3 = 150, f4 = 150;System.out.println(f1 == f2);System.out.println(f3 == f4);}
}如果不明就里很容易認為兩個輸出要么都是true要么都是false。
首先需要注意的是f1、f2、f3、f4四個變量都是Integer對象引用,
所以下面的==運算比較的不是值而是引用。裝箱的本質是什么呢?
當我們給一個Integer對象賦一個int值的時候,會調用Integer類的靜態方法valueOf,如果看看valueOf的源代碼就知道發生了什么。public static Integer valueOf(int i) {if (i >= IntegerCache.low && i <= IntegerCache.high)return IntegerCache.cache[i + (-IntegerCache.low)];return new Integer(i);}IntegerCache源碼:
?private static class IntegerCache {static final int low = -128;static final int high;static final Integer cache[];static {// high value may be configured by propertyint h = 127;String integerCacheHighPropValue =sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");if (integerCacheHighPropValue != null) {try {int i = parseInt(integerCacheHighPropValue);i = Math.max(i, 127);// Maximum array size is Integer.MAX_VALUEh = Math.min(i, Integer.MAX_VALUE - (-low) -1);} catch( NumberFormatException nfe) {// If the property cannot be parsed into an int, ignore it.}}high = h;cache = new Integer[(high - low) + 1];int j = low;for(int k = 0; k < cache.length; k++)cache[k] = new Integer(j++);// range [-128, 127] must be interned (JLS7 5.1.7)assert IntegerCache.high >= 127;}private IntegerCache() {}} 簡單的說,如果整型字面量的值在-128到127之間,那么不會new新的Integer對象,而是直接引用常量池中的Integer對象,所以上面的面試題中f1==f2的結果是true,而f3==f4的結果是false。 類和對象 面向對象的特征有哪些方面? 抽象:抽象是將一類對象的共同特征 總結出來構造類的過程,包括數據抽象 和行為抽象 兩方面。抽象只關注對象有哪些屬性和行為,并不關注這些行為的細節是什么。比如一個英雄類,抽象出了name,hp這些屬性,使得開發過程中更加易于理解 繼承:子類繼承父類,從而繼承了父類的方法和屬性 封裝:通常認為封裝是把數據和操作數據的方法綁定起來,對數據的訪問只能通過已定義的接口。最常見的是把屬性私有化 封裝在一個類里面,只能通過方法去訪問。 (可以想想普通洗衣機和全自動洗衣機的差別,明顯全自動洗衣機封裝更好因此操作起來更簡單;我們現在使用的智能手機也是封裝得足夠好的,因為幾個按鍵就搞定了所有的事情)。 多態性:多態性是指允許不同子類的對象對同一消息作出不同的響應。 多態分操作符的多態和類的多態。 類的多態指父類引用指向子類對象,并且有繼承,有重寫。 運行時的多態是面向對象最精髓的東西,要實現多態需要做兩件事: 對象造型(用父類型引用子類型對象,這樣同樣的引用調用同樣的方法就會根據子類對象的不同而表現出不同的行為)。 方法重寫(子類繼承父類并重寫父類中已有的或抽象的方法) 訪問修飾符public,private,protected,以及不寫(默認)時的區別? 修飾符當前類同 包子 類其他包 public √ √ √ √ protected √ √ √ × default √ √ × × private √ × × ×
為什么不能使用private、protected修飾外部類為什么不能用private修飾Java外部類? 因為如果使用private修飾Java外部類,那么這個類不能創建實例,這個類的屬性和方法不能被訪問,那么創建這個類毫無意義,所以不能使用private修飾Java外部類。為什么不能用protected修飾Java外部類? 舉個栗子,如果類A用protected修飾,與類A不同包的類B想要訪問類A的話,類B就必須是繼承類A的(或者說類B必須為類A的子類),但是類B繼承類A的前提又是類B可以訪問到類A,仔細想想會發現這里是沖突的,其實這就說明了為什么不能用protected來修飾外部類。再說,protected是用來表示在繼承關系中的訪問權限的,在同一個包和子類中都可以訪問,因為繼承就是為了擁有父類的屬性和方法,所以protected是用于修飾類的方法和屬性的,也就是說,我想要這個類的屬性和方法可以被任何子類繼承,我就用protected。我想要這個類的屬性和方法不能被任何子類繼承,我就用private。同理,我想要這個類被繼承,我就用abstract。我不想這個類被繼承,我就用final。所以,用protected修飾類有什么意義呢?關鍵點還是在于第一句話,protected是用來表示在繼承關系中的訪問權限的! 成員的訪問控制
public protected default(默認不寫) private
主要區別:1.可以供給其他項目用:public2.本項目用2.其他包也可以用:protected2.本包用:default1.本類使用:private1.類
public
protected(不能創建protected修飾的類,用它還不如用abstract,自身項目會用,不給其他人用就可以)
default(留給同包的類使用,不是給其他類使用的,總結就是自用,什么情況下會創建自用的類?)普通類(不太多) class StringCharBuffer {}接口(不太多) interface InetAddressImpl {}抽象類(少) abstract class AbstractPlainDatagramSocketImpl {}有final修飾的(很少) final class ZoneRegion extends ZoneId implements Serializable {}、final class Finalizer {}
private(不能創建private修飾的類)2.成員變量publicprotected:極少見用protected修飾,哪里用到了再記錄一下default1.HashMap.class中的幾個成員變量都是defaultprivate1.URI.class中有大量用private修飾的字段
3.方法4.內部類:基本都是default、private修飾例一:public class HashMap<K,V>{static class Node<K,V> implements Map.Entry<K,V> {final int hash;final K key;V value;Node<K,V> next;}static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {TreeNode<K,V> parent; // red-black tree linksTreeNode<K,V> left;TreeNode<K,V> right;TreeNode<K,V> prev; // needed to unlink next upon deletionboolean red;}}例二:public class Base64 {private static class EncOutputStream extends FilterOutputStream {private int leftover = 0;private int b0, b1, b2;private boolean closed = false;...}private static class DecInputStream extends InputStream {private final InputStream is;private final boolean isMIME;private final int[] base64; // base64 -> byte mappingprivate int bits = 0; // 24-bit buffer for decodingprivate int nextin = 18; // next available "off" in "bits" for input;...}}例三:public class InetAddress implements java.io.Serializable {static final int IPv4 = 1;static final int IPv6 = 2;/* Specify address family preference */static transient boolean preferIPv6Address = false;static class InetAddressHolder {String originalHostName;InetAddressHolder() {}String hostName;}}
為什么要使用克隆? 想對一個對象進行處理,又想保留原有的數據進行接下來的操作,就需要克隆了。克隆分淺克隆和深克隆。 淺克隆后的對象中非基本對象和原對象指向同一塊內存,因此對這些非基本對象的修改會同時更改克隆前后的對象。 深克隆可以實現完全的克隆,可以用反射的方式或序列化的方式 實現。如何實現對象克隆? 實現Cloneable接口并重寫Object類中的clone()方法; 實現Serializable接口,通過對象的序列化和反序列化實現克隆,可以實現真正的深度克隆。 答:有兩種方式:java實現克隆的三種 時間和日期 時間和日期的一些類 目前Date類基本不用了,請放棄,一點不香 使用1.8?LocalDateTime 代替Date: LocalDateTime - 廖雪峰的官方網站JDK8的LocalDateTime用法 - 閑人鶴 - 博客園 獲取當前此刻的時間 構造一個指定年、月、日的時間: 修改日期 格式化日期及字符串解析 ? // 1.ZoneIdZoneId defaultZoneId = ZoneId.systemDefault();ZoneId localZoneId1 = ZoneId.of("CTT", ZoneId.SHORT_IDS);ZoneId localZoneId2 = ZoneId.of(ZoneId.SHORT_IDS.get("CTT"));ZoneId localZoneId3 = ZoneId.of("Asia/Shanghai");ZoneId localZoneId4 = ZoneId.of("GMT-8");ZoneId localZoneId5 = ZoneId.of("GMT+8");ZoneId localZoneId6 = ZoneId.of("UTC+8");ZoneId localZoneId7 = ZoneId.of("UTC-8");ZoneId localZoneId8 = ZoneId.of("+8");System.out.println(defaultZoneId);System.out.println(localZoneId1);System.out.println(localZoneId2);System.out.println(localZoneId3);System.out.println(localZoneId4);System.out.println(localZoneId5);System.out.println(localZoneId6);System.out.println(localZoneId7);System.out.println(localZoneId8);// 2.ZoneOffsetZoneOffset zos1 = ZoneOffset.ofHours(0);ZoneOffset zos2 = ZoneOffset.ofHours(8); // 相對于0時區(格林威治時間、GMT+0)時間而言ZoneOffset zos3 = ZoneOffset.MAX;ZoneOffset zos4 = ZoneOffset.MIN;ZoneOffset zos5 = ZoneOffset.of("+8");ZoneId localZoneId9 = ZoneId.ofOffset("GMT", zos1);// 3.ZoneDateTimeZonedDateTime zdt = ZonedDateTime.now();System.out.println(zdt);ZonedDateTime zdt2 = ZonedDateTime.now(localZoneId1);System.out.println(zdt2);ZonedDateTime zdt3 = ZonedDateTime.now(localZoneId2);System.out.println(zdt3);ZonedDateTime zdt4 = ZonedDateTime.now(zos2);System.out.println("zos1"+zdt4);ZonedDateTime zdt5 = ZonedDateTime.now(zos5);System.out.println("zos2"+zdt5);Date date1 = new Date().from(zdt.toInstant()); // ZoneDateTime轉DateZonedDateTime.ofInstant(date1.toInstant(), ZoneId.of("GMT+8")); // Date轉ZoneDateTimeSystem.out.println("------------------");// 4.LocalDateTimeLocalDateTime localDateTime=LocalDateTime.now();Date date = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant()); // LocalDateTime轉DateLocalDateTime localDateTime1 = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(); // Date轉LocalDateTime// 系統當前時區
ZoneId defaultZoneId = ZoneId.systemDefault();
/*
關于舊的日期類Date和新的日期類LocalDateTime的轉換問題:
Java 8中 java.util.Date 類新增了兩個方法,分別是from(Instant instant)和toInstant()方法
這兩個方法使我們可以方便的實現將舊的日期類轉換為新的日期類,具體思路都是通過Instant當中介(instant 瞬間; 片刻; 某一時刻;),
然后通過Instant來創建LocalDateTime(這個類可以很容易獲取LocalDate和LocalTime),
新的日期類轉舊的也是如此,將新的先轉成LocalDateTime,然后獲取Instant,接著轉成Date
*/
// 1.Date轉換為LocalDateTime、LocalDate、LocalTime
System.out.println("--- Date轉換為LocalDateTime、LocalDate ---");
Date date1 = new Date();
Instant i1= date1.toInstant();
ZonedDateTime zdt = ZonedDateTime.ofInstant(i1, ZoneId.systemDefault());
ZonedDateTime zdt2 = ZonedDateTime.ofInstant(i1, ZoneId.of("Asia/Shanghai"));
ZonedDateTime zdt3 = ZonedDateTime.ofInstant(i1, ZoneId.of(ZoneId.SHORT_IDS.get("CTT"))); // 中國
ZonedDateTime zdt4 = ZonedDateTime.ofInstant(i1, ZoneId.of("GMT")); // 格林威治時間 (GMT)
ZonedDateTime zdt5 = ZonedDateTime.ofInstant(i1, ZoneId.of("GMT+1")); // 東二區
ZonedDateTime zdt6 = ZonedDateTime.ofInstant(i1, ZoneId.of("GMT+8")); // 東八區 中國屬于東八區
ZonedDateTime zdt7 = ZonedDateTime.ofInstant(i1, ZoneId.of("UTC")); // 世界協調時間 (UTC)
ZonedDateTime zdt8 = ZonedDateTime.ofInstant(i1, ZoneId.of("UTC+8")); // 世界協調時間 (UTC)
ZonedDateTime zdt9 = ZonedDateTime.ofInstant(i1, ZoneOffset.of("+8"));
ZonedDateTime zdt10= ZonedDateTime.ofInstant(i1, ZoneOffset.of("CTT", ZoneId.SHORT_IDS));LocalDateTime localDateTime = zdt.toLocalDateTime();
LocalDate localDate = zdt.toLocalDate();
LocalTime localTime = zdt.toLocalTime();// 2.LocalDateTime、LocalDate、LocalTime轉換為Date
System.out.println("--- LocalDateTime、LocalDate轉換為Date ---");
LocalDateTime ldt = LocalDateTime.now();
LocalDate ld = LocalDate.now();
LocalTime lt = LocalTime.now();
Date date2 = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant());
Date date3 = Date.from(ld.atStartOfDay(ZoneId.systemDefault()).toInstant());
Date dateT = Date.from(LocalDateTime.of(ld, lt).atZone(ZoneId.systemDefault()).toInstant());
System.out.println(new Date());
System.out.println(date2);
System.out.println(date3);
System.out.println(dateT);// 3.LocalDateTime、LocalDate轉換為字符串
System.out.println("--- LocalDateTime、LocalDate轉換為字符串 ---");
DateTimeFormatter dtf1 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
DateTimeFormatter dtf2= DateTimeFormatter.ofPattern("yyyy-MM-dd");
System.out.println(ldt.format(dtf1));
System.out.println(ld.format(dtf2));// 4.字符串轉化為LocalDateTime、LocalDate
System.out.println("--- 字符串轉化為LocalDateTime、LocalDate ---");
String date4 = "2021-08-21 12:23:11";
String date5 = "2021-08-21";
LocalDateTime ldt1 = LocalDateTime.parse(date4, dtf1);
LocalDate ld2 = LocalDate.parse(date5, dtf2);
System.out.println(ldt1);
System.out.println(ld2); LocalDateTime卻是線程安全的(Thisclassis immutable and thread-safe. (不可變、線程安全!)) 最新JDBC映射將把數據庫的日期類型和Java 8的新類型關聯起來:如何在Java 8中愉快地處理日期和時間 - 廖雪峰的官方網站 SQL -> Java
--------------------------
date -> LocalDate
time -> LocalTime
datatime -> LocalDateTime
timestamp -> LocalDateTime
使用DateTimeFormatter 代替SimpleDateFormat SimpleDateFormat最主要的致命問題也是在于它本身并不線程安全 DateTimeFormatter了,他也是線程安全的 java.util 包提供了 Date 類來封裝當前的日期和時間。其實很多方法都不用了 第一個構造函數使用當前日期和時間來初始化對象。Date( ) 第二個構造函數接收一個參數,該參數是從1970年1月1日起的毫秒數。Date(long millisec) long time = System.currentTimeMillis();
Date date = new Date(time);
System.out.println(date); 使用 SimpleDateFormat 可以將日期轉化為字符串,可以將字符串轉化為日期 使用printf格式化日期 Calendar類 我們現在已經能夠格式化并創建一個日期對象了,但是我們如何才能設置和獲取日期數據的特定部分呢,比如說小時,日,或者分鐘? 我們又如何在日期的這些部分加上或者減去值呢? 答案是使用Calendar 類。 Calendar類的功能要比Date類強大很多,而且在實現方式上也比Date類要復雜一些。 Calendar類是一個抽象類,在實際使用時實現特定的子類的對象,創建對象的過程對程序員來說是透明的,只需要使用getInstance方法創建即可。 GregorianCalendar類Calendar類實現了公歷日歷 ,GregorianCalendar是Calendar類的一個具體實現。 Calendar 的getInstance()方法返回一個默認用當前的語言環境和時區初始化的GregorianCalendar對象。GregorianCalendar定義了兩個字段:AD和BC。這是代表公歷定義的兩個時代。 關于時間工具類TimeUnit 時間粒度的使用 第一.提供了便捷的方法用于把時間轉換成不同單位,如將天轉換為小時、小時轉為分鐘、秒轉換為毫秒等TimeUnit.DAYS.toHours(1)//時間轉換 第二.提供了可讀性更好 的線程暫停操作,通常用來替換Thread.sleep(); TimeUnit.MINUTES.sleep(4); // sleeping for 4 minutes關于時間工具類ChronoUnit 的使用 如:ChronoUnit.DAYS.between(LocalDate.now(), expireTime)時間戳轉秒、分、小時、天 時間戳(毫秒)System.currentTimeMillis() 時間戳(轉秒)System.currentTimeMillis() / 1000 時間戳(轉分鐘)System.currentTimeMillis() / 1000 / 60 時間戳(轉小時)System.currentTimeMillis() / 1000 / (60 * 60) 時間戳(轉天) System.currentTimeMillis() / 1000 / (60 * 60 * 24) 或者 long currentMilliseconds = new Date().getTime(); 或者使用jdk 1.8?的日期 Long currentSecond = LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8"));
Long currentMilliSecond = LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli();
ZonedDateTime zdt = ZonedDateTime.of(LocalDateTime.now(), ZoneOffset.of("+8"));
Long currentSecond2 = zdt.toInstant().getEpochSecond();
Long currentMilliSecond2 = zdt.toInstant().toEpochMilli();
繼承和多態 構造器(constructor)是否可被重寫(override)? 答:構造器不能被繼承,因此不能被重寫,但可以被重載。一個類的構造函數允許有多個,比如無參構造,有參構造,使用的就是重載。重載(Overload)和重寫(Override)的區別。重載的方法能否根據返回類型進行區分? 答:方法的重載和重寫都是實現多態的方式,區別在于前者實現的是編譯時的多態性,而后者實現的是運行時的多態性。重載發生在一個類中 ,同名的方法如果有不同的參數列表(參數類型不同、參數個數不同或者二者都不同)則視為重載;重載對返回類型沒有特殊的要求。重寫發生在子類與父類之間 ,重寫要求子類被重寫方法與父類被重寫方法有相同的返回類型,比父類被重寫方法更好訪問,不能比父類被重寫方法聲明更多的異常(里氏代換原則)。抽象類必須要有抽象方法嗎? 不需要,抽象類不一定非要有抽象方法。普通類和抽象類有哪些區別? 普通類不能包含抽象方法,抽象類可以包含抽象方法。 抽象類不能直接實例化,普通類可以直接實例化。 抽象類能使用 final 修飾嗎? 不能,定義抽象類就是讓其他類繼承的,如果定義為 final 該類就不能被繼承,這樣彼此就會產生矛盾,所以 final 不能修飾抽象類,如下圖所示,編輯器也會提示錯誤信息抽象的(abstract)方法是否可同時是靜態的(static),是否可同時被synchronized修飾? 答:都不能。抽象方法需要子類重寫,而靜態的方法是無法被重寫的,因此二者是矛盾的。synchronized和方法的實現細節有關,抽象方法不涉及實現細節,因此也是相互矛盾的。必須掌握的hashcode()方法 hashcode是什么? hash和hash表是什么? 想要知道這個hashcode,首先得知道hash,通過百度百科看一下: hash是一個函數,該函數中的實現就是一種算法,就是通過一系列的算法來得到一個hash值 。這個時候,我們就需要知道另一個東西,hash表,通過hash算法得到的hash值就在這張hash表中,也就是說,hash表就是所有的hash值組成的,有很多種hash函數,也就代表著有很多種算法得到hash值,如上面截圖的三種,等會我們就拿第一種來說。 hashcode 有了前面的基礎,這里講解就簡單了,hashcode就是通過hash函數得來的,通俗的說,就是通過某一種算法得到的,hashcode就是在hash表中有對應的位置。每個對象都有hashcode,對象的hashcode怎么得來的呢? 首先一個對象肯定有 物理地址 ,在別的博文中會hashcode說成是代表對象的地址,這里肯定會讓讀者形成誤區,對象的物理地址跟這個hashcode地址不一樣, hashcode代表對象的地址說的是對象在hash表中的位置,物理地址說的對象存放在內存中的地址 ,那么對象如何得到hashcode呢? 通過對象的內部地址(也就是物理地址)轉換成一個整數 ,然后該整數通過hash函數的算法就得到了hashcode。所以,hashcode是什么呢?就是在hash表中對應的位置。 這里如果還不是很清楚的話,舉個例子,hash表中有 hashcode為1、hashcode為2、(…)3、4、5、6、7、8這樣八個位置,有一個對象A,A的物理地址轉換為一個整數17(這是假如),就通過直接取余算法,17%8=1,那么A的hashcode就為1,且A就在hash表中1的位置。 肯定會有其他疑問,接著看下面,這里只是舉個例子來讓你們知道什么是hashcode的意義。
hashcode有什么作用呢? 前面說了這么多關于hash函數,和hashcode是怎么得來的,還有hashcode對應的是hash表中的位置,可能大家就有疑問,為什么hashcode不直接寫物理地址呢,還要另外用一張hash表來代表對象的地址?接下來就告訴你hashcode的作用, 1、HashCode的存在主要是為了查找的快捷性,HashCode是用來在散列存儲結構中確定對象的存儲地址的(后半句說的用hashcode來代表對象就是在hash表中的位置) 為什么hashcode就查找的更快,比如:我們有一個能存放1000個數這樣大的內存中,在其中要存放1000個不一樣的數字,用最笨的方法,就是存一個數字,就遍歷一遍,看有沒有相同得數,當存了900個數字,開始存901個數字的時候,就需要跟900個數字進行對比,這樣就很麻煩,很是消耗時間,用hashcode來記錄對象的位置,來看一下。 hash表中有1、2、3、4、5、6、7、8個位置,存第一個數,hashcode為1,該數就放在hash表中1的位置,存到100個數字,hash表中8個位置會有很多數字了,1中可能有20個數字,存101個數字時,他先查hashcode值對應的位置,假設為1,那么就有20個數字和他的hashcode相同,他只需要跟這20個數字相比較(equals),如果每一個相同,那么就放在1這個位置,這樣比較的次數就少了很多,實際上hash表中有很多位置,這里只是舉例只有8個,所以比較的次數會讓你覺得也挺多的,實際上,如果hash表很大,那么比較的次數就很少很少了。 通過對原始方法和使用hashcode方法進行對比,我們就知道了hashcode的作用,并且為什么要使用hashcode了 equals方法和hashcode的關系? 通過前面這個例子,大概可以知道,先通過hashcode來比較,如果hashcode相等,那么就用equals方法來比較兩個對象是否相等。 用個例子說明:上面說的hash表中的8個位置,就好比8個桶,每個桶里能裝很多的對象,對象A通過hash函數算法得到將它放到1號桶中,當然肯定有別的對象也會放到1號桶中,如果對象B也通過算法分到了1號桶,那么它如何識別桶中其他對象是否和它一樣呢,這時候就需要equals方法來進行篩選了。 如果兩個對象equals相等,那么這兩個對象的HashCode一定也相同 如果兩個對象的HashCode相同,不代表兩個對象就相同,只能說明這兩個對象在散列存儲結構中,存放于同一個位置 為什么equals方法重寫的話,建議也一起重寫hashcode方法?如果對象的equals方法被重寫,那么對象的HashCode方法也盡量重寫 舉個例子,其實就明白了這個道理, 比如:有個A類重寫了equals方法,但是沒有重寫hashCode方法,看輸出結果,對象a1和對象a2使用equals方法相等,按照上面的hashcode的用法,那么他們兩個的hashcode肯定相等,但是這里由于沒重寫hashcode方法,他們兩個hashcode并不一樣,所以,我們在重寫了equals方法后,盡量也重寫了hashcode方法,通過一定的算法,使他們在equals相等時,也會有相同的hashcode值。 實例:String就重寫了equals和hashcode方法 hashCode和equals java對象hashCode存在的必要性是什么?hashCode是怎樣計算的??java對象hashCode存在的必要性是什么?_讀書就是賺錢-CSDN博客_java對象hash碼有什么用 java中有哪些地方涉及到了hash函數? String對象重寫了hashCode()方法,同樣也重寫了equal() HashMap定義了一個對key求hash的方法 必須掌握的hashcode()方法:hashcode詳解 - 有夢想的老王 - 博客園 hash是一個函數,該函數中的實現就是一種算法,就是通過一系列的算法來得到一個hash值, hashcode有什么作用呢? equals方法和hashcode的關系? 為什么equals方法重寫的話,建議也一起重寫hashcode方法? ==和equals的區別 對于==比較的是值是否相等 如果作用于引用類型 的變量,則比較的是所指向的對象的地址 ?如果作用于基本數據類型 的變量,則直接比較其存儲的 “值”是否相等; 對于equals方法,注意:equals方法不能作用于基本數據類型的變量,equals繼承Object類,比較的是是否是同一個對象 諸如String 、Integer 、Date等類對equals方法進行了重寫的話,比較的是所指向的對象的內容 。 如果沒有對equals方法進行重寫,則比較的是引用類型的變量所指向的對象的地址 ; java中所有類的父類是Object類,在Object中,已經定義了一個equals()方法,但是這個默認的equals()方法實際上也只是測試兩個變量引用是否指向同一對象(即與 == 功能一樣) 。所以它并不一定能得到你所期望的效果。通常我們需要自己將定義的類(就是上面的TestEqual)中的equals()進行重寫。像Integer封裝類就已經重寫了Object中的equals()方法,直接可以拿來比較引用類型c和d指向的對象的值。 例子 例一 //基本類型比較int a = 100;int b = 100;System.out.println("a == b?" + (a == b));//true//引用類型比較Integer c = new Integer(100);Integer d = new Integer(100);System.out.println("c == d?" + (c == d));//falseSystem.out.println(c.equals(d));//true 例一:指向數據段中的字符串常量對象 String s1 = "java";
String s2 = "java";System.out.println(s1==s2); //true
System.out.println(s1.equals(s2)); //true 例二:指向堆中的字符串對象 String s1 = new String("java");
String s2 = new String("java");System.out.println(s1==s2); //false
System.out.println(s1.equals(s2)); //true
兩個對象值相同(x.equals(y) == true),但卻可有不同的hash code,這句話對不對? 答:不對,如果兩個對象x和y滿足x.equals(y) == true,它們的哈希碼(hash code)應當相同。 Java對于eqauls方法和hashCode方法是這樣規定的:(1)如果兩個對象相同(equals方法返回true),那么它們的hashCode值一定要相同;(2)如果兩個對象的hashCode相同,它們并不一定相同。兩個對象的 hashCode()相同,則 equals()也一定為 true,對嗎? 不對,兩個對象的 hashCode() 相同,equals() 不一定 true。指出下面程序的運行結果。 class A {static {System.out.print("1");}public A() {System.out.print("2");}
}class B extends A{static {System.out.print("a");}public B() {System.out.print("b");}
}public class Hello {public static void main(String[] args) {A ab = new B();ab = new B();}} 答:執行結果:1a2b2b。創建對象時構造器的調用順序是:先初始化靜態成員,然后調用父類構造器,再初始化非靜態成員,最后調用自身構造器。 接口和內部類 接口是否可繼承(extends)接口?抽象類是否可實現(implements)接口?抽象類是否可繼承具體類(concrete class)? 答:接口可以繼承接口,而且支持多重繼承。抽象類可以實現(implements)接口,抽象類可繼承具體類也可以繼承抽象類。接口和抽象類有什么區別? 具體含義的不同:抽象類更注重本質,接口更注重其具有什么樣的功能及其能充當什么樣的角色 實現:抽象類的子類使用 extends 來繼承;接口必須使用 implements 來實現接口。 構造函數:抽象類可以有構造函數;接口不能有。 實現數量:類可以實現很多個接口;但是只能繼承一個抽象類。 訪問修飾符:接口中的方法 默認使用 public 修飾;抽象類中的方法可以是任意訪問修飾符。 ? ? ? ? ? ? ? ? ? ? ? ?抽象類中可以定義成員變量 ,而接口中定義的成員變量實際上都是常量。 注意: 抽象類和接口都不能夠實例化,但可以定義抽象類和接口類型的引用。 一個類如果繼承了某個抽象類或者實現了某個接口都需要對其中的抽象方法全部進行實現 ,否則該類仍然需要被聲明為抽象類。接口比抽象類更加抽象,因為抽象類中可以定義構造器,可以有抽象方法和具體方法,而接口中不能定義構造器而且其中的方法全部都是抽象方法 。 jdk1.8?之后,在接口里面可以有非抽象的默認方法。 參考:Java 8 新特性 | 菜鳥教程?、納尼,java可以在接口中實現非抽象方法了? - 王若伊_恩賜解脫 - 博客園 在接口中,可以直接添加靜態方法。 在接口中,可以直接添加非抽象的實例方法。 在實例方法的申明中,需要增加default關鍵字修飾,因此這種方法也稱為默認方法。他是接口自帶的方法。接口被實現后,實例可以直接使用這些默認方法,同時如果對默認方法需要重寫時,可以直接重寫即可。 靜態嵌套類(Static Nested Class)和成員內部類(Inner Class)的不同? 答:靜態嵌套類又稱為頂級嵌套類,其本質只是一個放在其它類的不同類,它可以不依賴于外部類實例被實例化。 而通常的內部類需要在外部類實例化后才能實例化。成員內部類可以引用它的包含類(外部類)的成員嗎?有沒有什么限制? 答:一個內部類對象可以訪問創建它的外部類對象的成員,包括私有成員。匿名內部類是否可以繼承其它類?是否可以實現接口? 答:可以繼承其他類或實現其他接口是否可以從一個靜態(static)方法內部發出對非靜態(non-static)方法的調用? 答:不可以,靜態方法只能訪問靜態成員,因為非靜態方法的調用要先創建對象,在調用靜態方法時可能對象并沒有被初始化。一個".java"源文件中是否可以包含多個類(不是內部類)?有什么限制? 答:可以,但一個源文件中最多只能有一個公開類(public class)而且文件名必須和公開類的類名完全保持一致。說出JAVA中一些常用的類,包,接口,請各舉5個 類: 1.java.lang.Object 2.java.lang.String 3.java.lang.Math 4.java.lang.System 5.java.util.Date 6.java.io.File 7.java.io.FileInputStream 8.java.io.FileOutputStream 包: 1.java.lang包:Java編程語言的基本類庫 2.java.io包:提供了通過數據流、對象序列以及文件系統實現的系統輸入、輸出 3.java.util包:包括集合類、時間處理模式、日期時間工具等各種常用工具包 4.java.sql包:提供了訪問和處理來自Java標準數據源數據的類 5.java.math包:提供了簡明的整數算術以及十進制算術的基本函數 接口: 1.java.util.List<E> 2.java.util.Map<E> 3.java.util.Iterator<E> 4.java.sql.CallableStatement 5.java.lang.Comparable<T> 6.java.lang.Cloneable 7.java.io.serializable 設計:抽象類還是接口
接口
為什么要有接口?哪些類需要聲明為接口?
實例變量:變量:public static final int a = 5;
方法:接口可以有非抽象方法嗎?jdk1.8之后可以有
抽象類
為什么要有抽象類?哪些類需要聲明為抽象類?
接口和抽象類的區別:可以著重研究Java集合框架
語法層次的區別
設計層次的區別
例子:從一扇“門”,到千扇“門”
public abstract void openDoor();//開門
public abstract void closeDoor();//關門
public abstract void lockDoor();//上鎖
public abstract void unlockDoor();//解鎖
public abstract void ringBell();//按門鈴
抽象類實現接口的意義何在? 輸入/輸出 什么是IO流? Java中所有的IO都是通過流來實現的,可以將流理解為連接到數據目標或源的管道 , 可以通過連接到源的流從源當中讀取數據,或通過連接到目標的流向目標中寫入數據。Java中有幾種類型的流? 字節流和字符流。字節流繼承于InputStream、OutputStream,字符流繼承于Reader、Writer。Java中流類的超類主要由那些? java.io.InputStream java.io.OutputStream java.io.Reader java.io.Writer 字節流和字符流的區別 字節流以字節 為基本單位來處理數據的輸入、輸出,一般用于對二進制 的讀寫,如聲音、圖像等。 字符流以字符 為基本單位來處理數據的輸入、輸出,一般用于對文本類型數據 的讀寫,如文本文件、網絡中發送的文本信息等。 雖然文本數據也可以看做二進制數據,但一般采用字符流處理文本數據比采用字節流效率更高,也更方便。 FileInputStream和FileOutputStream是什么? 這是在拷貝文件操作 的時候,經常用到的兩個類。 在處理小文件的時候,它們性能表現還不錯,在大文件的時候,最好使用BufferedInputStream (或 BufferedReader) 和BufferedOutputStream (或 BufferedWriter)標準的I/O流 System.in.println()是什么?系統輸入流 System是一個java.lang包中的類,用于和底層的操作系統進行交互; in是System類的一個靜態成員,類型為InputStream (系統輸入流); println是InputStream的一個方法。 注意:現在我們使用Scan類 作為系統輸入流更方便。System.out.println()是什么?系統輸出流 System是一個java.lang包中的類,用于和底層的操作系統進行交互; out是System類的一個靜態成員,類型為PrintStream (系統輸出流); println是PrintStream的一個方法。說說PrintStream和PrintWriter的異同點? 他們兩個的功能相同,但是屬于不同的分類; 字節流和字符流。他們都有println()方法。什么是Filter流? Filter Stream是一種IO流。 Filter流的主要作用是:對存在的流增加一些額外的功能 ,像給目標文件增加源文件中不存在的行數,或者增加拷貝的性能。 有哪些可用的Filter流? 在java.io包中主要由4個可用的filter Stream組成。兩個字節filter stream,兩個字符filter stream。分別是:FilterInputStream、FilterOutputStream、FilterReader和FilterWriter。 SequenceInputStream的作用是什么? 使用很少的代碼,實現‘拷貝多個文件到一個目標文件 ’。說說RandomAccessFile? 它在java.io包中是一個特殊的類,既不是輸入流也不是輸出流,它兩者都可以做到。 它是Object的直接子類 。通常來說,一個流只有一個功能,要么讀,要么寫。但是RandomAccessFile類既可以讀文件,也可以寫文件。 DataInputStream和DataOutputStream有的方法,在RandomAccessFile中都存在。 在文件拷貝 的時候,哪一種流可用于提升更多的性能? 在字符流的時候,使用BufferedReader和BufferedWriter。 在字節流的時候,使用BufferedInputStream和BufferedOutputStream。 說說管道流(Piped Stream) 在多個線程或進程中傳遞數據 的時候管道流非常有用。 有四種管道流:PipedInputStream、PipedOutputStream、PipedReader和PipedWriter。 BIO、NIO、AIO 有什么區別? BIO:Block IO 同步阻塞式 IO,就是我們平常使用的傳統 IO,它的特點是模式簡單使用方便,并發處理能力低 NIO:Non IO 同步非阻塞 IO,是傳統 IO 的升級,客戶端和服務器端通過 Channel(通道)通訊,實現了多路復用。 AIO:Asynchronous IO 是 NIO 的升級,也叫 NIO2,實現了異步非堵塞 IO ,異步 IO 的操作基于事件和回調機制。 File File的常用方法都有哪些? 創建目錄: File類中有兩個方法可以用來創建文件夾: mkdir( ) 方法創建一個文件夾,成功則返回true,失敗則返回false。失敗表明File對象指定的路徑已經存在,或者由于整個路徑還不存在,該文件夾不能被創建。mkdirs() 方法創建一個文件夾和它的所有父文件夾 。File. exists():檢測文件路徑是否存在。 File. createFile():創建文件。 File. createDirectory():創建文件夾。 File. delete():刪除一個文件或目錄。 File. copy():復制文件。 File. move():移動文件。 File. size():查看文件個數。 File. read():讀取文件。 File. write():寫入文件。 windows和Linux文件路徑分隔符的不同及獲取 反斜杠”\”是在windows系統下文件路徑用到的斜杠,這個反斜杠在Linux系統下是不能用的。 斜杠”/”是在windows系統和Linux系統下都可以使用的斜杠,所有在java開發中盡量用“/”,這樣代碼到了Linux中也是沒有問題的。 當然也可以用代碼來獲取當前系統 所使用的斜杠類型,如下方式: ? File file = new File("f:"+File.separator+"log.txt");
System.out.println(File.separator); // 如果是在windows系統下運行 \
System.out.println(File.separator); // 如果是在linux服務器下運行 / 在liunx服務器上打印
java 在類里面獲取項目路徑 ,創建文件夾和文件
String path=Thread.currentThread().getContextClassLoader().getResource("").toString();System.out.println(path); // file:/F:/tools/eclipse/workspace/testSecurity/target/classes/path=path.replace("/", File.separator);path=path.replace("file:"+File.separator, ""); //去掉file:/?path=path.replace("classes"+File.separator, ""); //去掉classes/// 根據需要可以再去掉一些System.out.println(path); // F:\tools\eclipse\workspace\testSecurity\target\//文件添加下級目錄地址path+= "WEB-INF"+File.separator +"view" + File.separator +"image";File file = new File(path);if (!file.exists()) {file.mkdirs();}String filename = System.currentTimeMillis() + UUID.randomUUID().toString() + ".png";System.out.println(path); // F:\tools\eclipse\workspace\testSecurity\target\WEB-INF\view\imageFileImageOutputStream imageOutput = new FileImageOutputStream(new File(path + File.separator +filename));File fileSource = new File("f:/bg.jpg"); // 需要上傳的文件,文件后綴別忘記修改byte[] source = new byte[(int) fileSource.length()];FileInputStream fis = new FileInputStream(fileSource);fis.read(source);imageOutput.write(source, 0, source.length);imageOutput.close();fis.close(); 注意:在服務器中,文件只能放在項目目錄路徑下,無法放到項目之外的路徑。所以引發了一個問題:項目重新發布的時候,必定要將舊的項目刪除,導致之前上傳的文件也被刪除,所以最好將需要上傳的文件上傳到其他的地方,例如阿里云oss,以保證持久化。那為什么有這么一個需求呢?因為需要將一個文件夾(含有多個文件)壓縮為zip文件,而oss無法將文件夾壓縮,所以只好在項目本地壓縮好再上傳,那么之后再刪除也沒有影響了
序列化 什么是序列化?什么是反序列化?有什么用?怎么使用?利與弊? 序列化是指對象通過把自己轉化為一系列字節,記錄字節的狀態數據,以便再次利用。 它又稱持久化,將其寫入磁盤中。序列化:實現Serializable接口是為了告訴jvm這個類的對象可以被序列化,實現了Serializable接口 的對象通過ObjectOutputStream對象輸出流 將對象轉化為一系列字節,存儲到磁盤。 反序列化:通過ObjectInputStream對象輸入流 將儲存在磁盤中的對象(字節形式)轉化為對象使用 一個類的對象要想序列化成功,必須滿足兩個條件: 該類必須實現 java.io.Serializable 對象。 該類的所有屬性必須是可序列化的。如果有一個屬性不是可序列化的,則該屬性必須注明是短暫的。 對于一個實體類,不想將所有的屬性都進行序列化,有專門的關鍵字 transient : private transient String name; 當對該類序列化時,會自動忽略被 transient 修飾的屬性。 Java中transient關鍵字的詳細總結_老鼠只愛大米的博客-CSDN博客_transient關鍵字 關于序列化要注意兩個問題: 性能問題 版本 java對象為什么要序列化? java對象為什么要序列化?_Neic的博客-CSDN博客_java對象為什么要序列化 序列化到底是為了什么,本質是什么? - 知乎 什么情況下需要Java序列化? 當Java對象需要在網絡上傳輸或者持久化存儲到文件中時,就需要對Java對象進行序列化處理。什么情況下需要序列化: 當你想把的內存中的對象狀態保存到一個文件中或者數據庫中時候; 當你想用套接字在網絡上傳送對象的時候; 當你想通過RMI傳輸對象的時候。 關于serialVersionUID 在反序列化的時候,類和serialVersionUID都要和序列化的時候一致。多個類的serialVersionUID一樣也能反序列化,最好唯一。 Java 序列化 serialVersionUID 不一致問題解決方案 - 墨天輪 示例 序列化可以保持對象的狀態。 一般是分布式傳遞對象,或者網絡傳輸的時候。需要序列化。 典型的一個應用: tomcat關閉以后會把session對象序列化到SESSIONS.ser文件中,等下次啟動的時候就把這些session再加載到內存里面來。 緩存:Hibernate POJO為什么要實現Serializable接口 hibernate有二級緩存,緩存會將對象寫進硬盤,就必須序列化,以及兼容對象在網絡中的傳輸 等等。 pojo實現序列化的原因是pojo對象需要持久化存儲到數據庫,或者被它的緩存存儲在硬盤中。如果pojo存在于分布式環境中,也必須序列化。 一個說法: 游戲玩過沒 游戲都有存檔吧 一個存檔就看作一個序列化文件吧 你游戲存檔能干點啥那,可以拷貝給別人玩吧,可以留著以后玩吧 異常處理 Error和Exception有什么區別? error---錯誤 : 是指程序無法處理的錯誤,表示應用程序運行時出現的重大錯誤 。例如系統崩潰、jvm運行時出現的OutOfMemoryError以及Socket編程時出現的端口占用等程序無法處理的錯誤。 Exception --- 異常 :表示程序可以處理的異常,可以捕獲,且能恢復。異常可分為運行時異常(非檢查性異常)和編譯異常(檢查性異常)。 運行時異常與編譯異常的區別 運行時異常 是不需要捕獲的,程序員可以不去處理,當異常出現時,虛擬機會處理。 我們常見的5中運行時異常: ClassCastException(類轉換異常) IndexOutOfBoundsException(數組越界) NullPointerException(空指針異常) ArrayStoreException(數據存儲異常,操作數組時類型不一致) 還有IO操作的BufferOverflowException異常 ?ArithmeticException(算術異常) 編譯異常 就必須得捕獲了,否則編譯不過去,java編譯器要求程序員必須對這種異常進行catch,Java認為Checked異常都是可以被處理(修復)的異常,所以Java程序必須顯式處理Checked異常。 常見的非運行異常有io異常和sql異常。 IOException、FileNotFoundExcetion 和SQLException Java語言如何進行異常處理,關鍵字:try、catch、finally、throws、throw分別如何使用? try和catch捕獲異常 try語句塊用于指出可能出現異常的區域,隨后跟上一個或者多個catch語句塊,在catch語句塊中編寫相應異常的處理程序。 try語句塊只能有一個,而catch語句塊可以有任意多個;catch語句塊緊跟在try語句塊之后,而且catch語句塊之間不能有任何代碼(注釋除外) finally語句塊: finally語句塊中的代碼無論在什么情況下都將保證執行,即使在try或cathc語句中有return語句執行,在離開前也要執行finally語句塊。雖然說是保證執行,但也有幾個特殊的情況可能會中斷finally語句的執行。 計算機斷電。 finally語句塊中執行了“System.exit();”方法。 執行finally語句塊的線程死亡。 finally語句塊里面產生異常。 finally語句塊最多只能有一個,也可以一個都沒有。 finally語句塊一般位于try-catch語句塊的后邊。 try-catch和finally語句塊之間需要注意的問題: 無catch時finally必須緊跟try。 catch與finally不能同時省略。 try、catch以及finally塊之間不能插入任何其他代碼。 throw出現在方法體 中,用于明確地拋出異常。 throws出現在方法的聲明中 ,表示該方法可能會拋出的異常。 throws和throw的區別 throws出現在方法的聲明中 ,表示該方法可能會拋出的異常,允許throws后面跟著多個異常類型? throw出現在方法體 中,用于明確地拋出異常。當方法在執行過程中遇到異常情況時,將異常信息封裝為異常對象,然后throw。?? 闡述final、finally、finalize的區別。 final:修飾符(關鍵字)有三種用法: 修飾類:表示該類不能被繼承; 修飾方法:表示方法不能被重寫; 修飾變量:表示變量只能一次賦值以后值不能被修改(常量)。 finally:通常放在try…catch…的后面構造總是執行代碼塊,這就意味著程序無論正常執行還是發生異常,這里的代碼只要JVM不關閉都能執行,可以將釋放外部資源的代碼寫在finally塊中。 ?finalize:Object類中定義的方法,Java中允許使用finalize()方法在垃圾收集器將對象從內存中清除出去之前做必要的清理工作。這個方法是由垃圾收集器在銷毀對象時調用的,通過重寫finalize()方法可以整理系統資源或者執行其他清理工作。 try{}里有一個return語句,那么緊跟在這個try后的finally{}里的代碼會不會被執行,什么時候被執行,在return前還是后? 答:會執行,在方法返回調用者前執行。 注意:在finally中改變返回值的做法是不好的,因為如果存在finally代碼塊,try中的return語句不會立馬返回調用者,而是記錄下返回值待finally代碼塊執行完畢之后再向調用者返回其值,然后如果在finally中修改了返回值,就會返回修改后的值。類ExampleA繼承Exception,類ExampleB繼承ExampleA。 有如下代碼片斷: try {throw new ExampleB("b")
} catch(ExampleA e){System.out.println("ExampleA");
} catch(Exception e){System.out.println("Exception");
}
請問執行此段代碼的輸出是什么? 答:輸出:ExampleA。父類可以捕獲到子類的異常。面試題 - 說出下面代碼的運行結果。(此題的出處是《Java編程思想》一書)
class Annoyance extends Exception {}
class Sneeze extends Annoyance {}class Human {public static void main(String[] args) ?throws Exception {try {try {throw new Sneeze();}?catch ( Annoyance a ) {//Annoyance a=new Sneeze();這一句使用的是父類的引用,但實際上是子類的對象,這是java中多態的經典表現。System.out.println("Caught Annoyance");throw a;}}?catch ( Sneeze s ) {System.out.println("Caught Sneeze");return ;}finally {System.out.println("Hello World!");}}
}
打印結果: Caught Annoyance Caught Sneeze Hello World! 對比上題就知道異同
class Annoyance extends Exception {}
class Sneeze extends Annoyance {}public class Test {public static void main(String[] args) {try {throw new Annoyance();}catch(Sneeze s) {System.out.println("caught Sneeze");return;}catch(Annoyance a) {System.out.println("caught Annoyance");}finally {System.out.println("hello world");}}} 打印結果: caught Annoyance hello world
Java中顯示try catch捕獲異常信息(異常類別、行號) 在實際開發過程中有的時候,我們需要對比如:IO操作的代碼塊 進行try異常處理,這個時候通過catch很好的解決了返回異常信息給前端。但是會產生一個新問題:隱藏了異常的真正原因,無法從根本上解決代碼異常問題,這個時候我們可以在catch里來獲取代碼塊真正異常的行號,異常原因等。 代碼如下: void getFile(String content){String path = "h:/web/1.h.t.m.l";File file = new File(path);try {FileOutputStream fos = new FileOutputStream(file, false);fos.write(content.getBytes(StandardCharsets.UTF_8));fos.close();} catch (Exception e) {StackTraceElement ste =e.getStackTrace()[0];log.error("======================================================");log.info("異常信息:"+e.getMessage());log.info("異常類:"+ste.getClassName());log.info("異常類名:"+ste.getFileName());log.info("異常行號:"+ste.getLineNumber());log.info("異常方法:"+ste.getMethodName());log.error("==================================================");}} 輸出結果:可以很明確地看到異常產生于path文件地址
1 JDBC Java連接mysql的較完整的url: jdbc:mysql://127.0.0.1:3306/sso?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000
&autoReconnect=true&useUnicode=true&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8 在這個MySQL的url中:
localhost:地址
3306:端口,默認是3306
newdb:數據庫名
useUnicode=true:true表示使用unicode編碼
characterEncoding=UTF-8:字符集
zeroDateTimeBehavior=convertToNull:java在連接mysql數據庫時,在操作值為0的timestamp類型時不能正確的處理,而是默認拋出一個異常,就是所見的:java.sql.SQLException:?Cannot?convert?value?'0000-00-00?00:00:00'?from?column?7?to?TIMESTAMP。這一問題在官方文檔中有詳細說明。
在JDBC連接串中有一項屬性:zeroDateTimeBehavior,可以用來配置出現這種情況時的處理策略,該屬性有下列三個屬性值:
l?exception:默認值,即拋出SQL?state?[S1009].?Cannot?convert?value....的異常;
l?convertToNull:將日期轉換成NULL值;
l?round:替換成最近的日期即0001-01-01;
因此對于這類異常,可以考慮通過修改連接串,附加zeroDateTimeBehavior=convertToNull屬性的方式予以規避,例如:
jdbc:mysql://localhost:3306/mydbname?zeroDateTimeBehavior=convertToNull
從另一個層面講,這類異常的觸發也與timestamp賦值的操作有關,如果能夠在設計階段和記錄寫入階段做好邏輯判斷,避免寫入 '0000-00-00 00:00:00'這類值,那么也可以避免出現 Cannot?convert?value?'0000-00-00?00:00:00'?from?column?N?to?TIMESTAMP的錯 誤。
useSSL=true:使用JDBC跟你的數據庫連接的時候,你的JDBC版本與MySQL版本不兼容,MySQL的版本更高一些,在連接語句后加上“useSSL=‘true’” ,就可以連接到數據庫了。 參數名稱 參數說明 缺省值 最低版本要求? user 數據庫用戶名(用于連接數據庫) 所有版本? passWord 用戶密碼(用于連接數據庫) 所有版本? useUnicode 是否使用Unicode字符集,如果參數characterEncoding設置為gb2312或gbk,本參數值必須設置為true false 1.1g? characterEncoding 當useUnicode設置為true時,指定字符編碼。比如可設置為gb2312或gbk false 1.1g? autoReconnect 當數據庫連接異常中斷時,是否自動重新連接? false 1.1? autoReconnectForPools 是否使用針對數據庫連接池的重連策略 false 3.1.3? failOverReadOnly 自動重連成功后,連接是否設置為只讀? true 3.0.12? maxReconnects autoReconnect設置為true時,重試連接的次數 3 1.1? initialTimeout autoReconnect設置為true時,兩次重連之間的時間間隔,單位:秒 2 1.1? connectTimeout 和數據庫服務器建立socket連接時的超時,單位:毫秒。 0表示永不超時,適用于JDK 1.4及更高版本 0 3.0.1? socketTimeout socket操作(讀寫)超時,單位:毫秒。 0表示永不超時 0 3.0.1?
闡述JDBC操作數據庫的步驟。 ??? 1.加載驅動。Class.forName("oracle.jdbc.driver.OracleDriver");2.創建連接。Connection con = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:orcl", "scott", "tiger");3.創建語句。PreparedStatement ps = con.prepareStatement("select * from emp where sal between ? and ?");ps.setInt(1, 1000);ps.setInt(2, 3000);4.執行語句。ResultSet rs = ps.executeQuery();5.處理結果。while(rs.next()) {System.out.println(rs.getInt("empno") + " - " + rs.getString("ename"));}6.關閉資源。finally {if(con != null) {try {con.close();} catch (SQLException e) {e.printStackTrace();}}} 提示:關閉外部資源的順序應該和打開的順序相反,也就是說先關閉ResultSet、再關閉Statement、在關閉Connection。上面的代碼只關閉了Connection(連接),雖然通常情況下在關閉連接時,連接上創建的語句和打開的游標也會關閉,但不能保證總是如此,因此應該按照剛才說的順序分別關閉。此外,第一步加載驅動在JDBC 4.0中是可以省略的(自動從類路徑中加載驅動),但是我們建議保留。Statement和PreparedStatement有什么區別?哪個性能更好? 和 Statement一樣,PreparedStatement也是用來執行sql語句的 與創建Statement不同的是,需要根據sql語句創建PreparedStatement PreparedStatement的優點1-參數設置 Statement?需要進行字符串拼接,可讀性和維護性比較差 PreparedStatement?使用參數設置,可讀性好,不易犯錯 PreparedStatement的優點2-性能表現 PreparedStatement有預編譯機制,當批量處理SQL或頻繁執行相同的查詢時,性能比Statement更快。 由于數據庫可以將編譯優化后的SQL語句緩存起來,下次執行相同結構的語句時就會很快(不用再次編譯和生成執行計劃)。 PreparedStatement的優點3-防止SQL注入式攻擊 使用JDBC操作數據庫時,如何提升讀取數據的性能?如何提升更新數據的性能? 要提升讀取數據 的性能,可以指定通過結果集(ResultSet)對象 的setFetchSize()方法指定每次抓取的記錄數(典型的空間換時間策略); 要提升更新數據 的性能可以使用PreparedStatement語句 構建批處理,將若干SQL語句置于一個批處理中執行。 在進行數據庫編程時,連接池有什么作用? 答:由于創建連接和釋放連接都有很大的開銷(尤其是數據庫服務器不在本地時,每次建立連接都需要進行TCP的三次握手,釋放連接需要進行TCP四次握手,造成的開銷是不可忽視的),為了提升系統訪問數據庫的性能,可以事先創建若干連接置于連接池中,需要時直接從連接池獲取,使用結束時歸還連接池而不必關閉連接,從而避免頻繁創建和釋放連接所造成的開銷,這是典型的用空間換取時間的策略(浪費了空間存儲連接,但節省了創建和釋放連接的時間)。池化技術在Java開發中是很常見的,在使用線程時創建線程池 的道理與此相同。 基于Java的開源數據庫連接池主要有:C3P0 、Proxool、DBCP、BoneCP、Druid 等。JDBC中如何進行事務處理? 答:Connection提供了事務處理的方法,通過調用setAutoCommit(false)可以設置關閉自動提交事務;當事務完成后用commit()顯式提交事務;如果在事務處理過程中發生異常則通過rollback()進行事務回滾。除此之外,從JDBC 3.0中還引入了Savepoint(保存點)的概念,允許通過代碼設置保存點并讓事務回滾到指定的保存點。事務的ACID是指什么? ? 原子性(Atomic):事務中各項操作,要么全做要么全不做,任何一項操作的失敗都會導致整個事務的失敗; 一致性(Consistent):事務結束后系統狀態是一致的; 隔離性(Isolated):并發執行的事務彼此無法看到對方的中間狀態; 持久性(Durable):事務完成后所做的改動都會被持久化,即使發生災難性的失敗。通過日志和同步備份可以在故障發生后重建數據。 反射 什么是類對象:就是用于描述這種類,有哪些構造函數,都有什么屬性,什么方法 作用:反射怎么做到不用知道類名,可以直接實例化類,不用硬編碼。高內聚,低耦合 通過類對象獲取構造函數信息、成員變量、獲取方法信息 獲取構造函數 /**
* 構造函數也是對象
* java.lang.Constructor中封裝了構造函數的信息
* getConstructor()方法獲取所有的public的構造函數
* getDeclaredConstructors得到所有的構造函數
*/
Constructor[] cs = c.getDeclaredConstructors();
for(Constructor constructor : cs){System.out.print(constructor.getName()+"(");//獲取構造函數的參數列表---》得到的是參數雷彪的類類型Class[] paramTypes = constructor.getParameterTypes();for(Class class1 : paramTypes){System.out.print(class1.getName()+",");}System.out.println(")");
}
獲取成員變量 /**
* 成員變量也是對象,是java.lang.reflect.Field這個類的的對象
* Field類封裝了關于成員變量的操作
* getFields()方法獲取的是所有public的成員變量的信息
* getDeclareFields()方法獲取的是該類自己聲明的成員變量的信息
*/
//1.獲取所有屬性
Field[] fs = c.getDeclaredFields();
for(Field field : fs){//得到成員變量的類型的類類型Class fieldType = field.getType();String typeName = fieldType.getName();//得到成員變量的名稱String fieldName = field.getName();System.out.print(typeName+" "+fieldName);
}
獲取方法信息 Class c = employee.getClass();//傳遞的是哪個子類的對象,c就是該子類的類類型
//1.獲取類的名稱
System.out.println("類的名稱是:"+c.getName());
//2.獲取類方法
Method[] ms = c.getMethods();//c.getDeclaredMethods();//2.1 得到方法的名稱
System.out.print(ms[i].getName()+"(");//2.2 得到方法的返回值類型的類類型
for(int i =0; i < ms.length; i++){Class retrunType = ms[i].getReturnType();System.out.print(retrunType.getName()+" ");
};//2.3 獲取的參數類型--->得到的是參數列表的類型的類類型
Class[] paraTypes = ms[i].getParameterTypes();for(Class class1 : paraTypes){System.out.print(class1.getName()+",");
};
獲得一個類的類對象 有哪些方式? 類型.class,例如:Employee.class 對象.getClass(),例如:employee.getClass() Class.forName(),例如:Class.forName("bean.Employee") 如何通過反射創建對象? 先拿到“類對象”:Class pClass=Class.forName(className); 然后通過類對象獲取“構造器對象” :Constructor c= pClass.getConstructor(); 再通過構造器對象創建一個對象:c.newInstance() 注意:現在不用獲去構造器對象就可以創建對象了:pClass.newInstance(); 如何通過反射訪問對象屬性 ? getField和getDeclaredField的區別 這兩個方法都是用于獲取字段 getField?只能獲取public的,包括從父類繼承來的字段。 getDeclaredField 可以獲取本類所有的字段,包括private的 ,但是不能獲取繼承來的字段。 (注: 這里只能獲取到private的字段,但并不能訪問該private字段的值,除非加上setAccessible(true) ) 如何通過反射調用對象的方法? // 獲取這個名字叫做setName,參數類型是String的方法Method m = h.getClass().getMethod("setName", String.class);// 對h對象,調用這個方法m.invoke(h, "蓋倫");// 使用傳統的方式,調用getName方法System.out.println(h.getName());
new 和 newInstance 的區別 初始化一個類,生成一個實例的時候;newInstance() 和 new 有什么區別?用newInstance與用new是區別的,區別在于創建對象的方式不一樣,前者是使用類加載機制,那么為什么會有兩種創建對象方式?這個就要從可伸縮、可擴展,可重用等軟件思想上解釋了。JAVA中工廠模式經常使用newInstance來創建對象,因此從為什么要使用工廠模式上也可以找到具體答案。例如:Class c = Class.forName(“A”);factory = (AInterface)c.newInstance();其中AInterface是A的接口,如果下面這樣寫,你可能會理解:String className = “A”;Class c = Class.forName(className);factory = (AInterface)c.newInstance();進一步,如果下面寫,你可能會理解:String className = readfromXMlConfig;//從xml 配置文件中獲得字符串Class c = Class.forName(className);factory = (AInterface)c.newInstance();上面代碼就消滅了A類名稱,優點:無論A類怎么變化,上述代碼不變,甚至可以更換A的兄弟類B , C , D….等,只要他們繼承Ainterface就可以。從jvm的角度看,我們使用new的時候,這個要new的類可以沒有加載;但是使用newInstance時候,就必須保證:1、這個類已經加載;2、這個類已經連接了。而完成上面兩個步驟的正是class的靜態方法forName()方法,這個靜態方法調用了啟動類加載器(就是加載javaAPI的那個加載器)。有了上面jvm上的理解,那么我們可以這樣說,newInstance實際上是把new這個方式分解為兩步,即,首先調用class的加載方法加載某個類,然后實例化。這樣分步的好處是顯而易見的。我們可以在調用class的靜態加載方法forName時獲得更好的靈活性,提供給了我們降耦的手段。[補充:]newInstance: 弱類型。低效率。只能調用無參構造。new: 強類型。相對高效。能調用任何public構造。newInstance()是實現IOC、反射、面對接口編程 和 依賴倒置 等技術方法的必然選擇,new 只能實現具體類的實例化,不適合于接口編程。里面就是通過這個類的默認構造函數構建了一個對象,如果沒有默認構造函數就拋出InstantiationException, 如果沒有訪問默認構造函數的權限就拋出IllegalAccessException可以看出,new 和 newInstance 具有相同的作用。
動態代理是什么?有哪些應用? 設計模式中的代理模式。 Spring ioc就是使用工廠模式,而工廠模式創建對象的邏輯就是利用反射。 spring aop采用了動態代理,動態代理的核心是發射。 關于Java 類加載器內容的詳解 邏輯與編程題 韓信帶兵不足百人,3人一列多1人;7人一行排列少2人;5人1行正好,請寫出代碼,以計算韓信帶了多少人? for(int a=1;a<100;a++) {if(a%5==0) {if(a%3==1&&a%7==5) {System.out.println(a);}}} 三名英國人和三個美國人都要過橋,但每次只能過兩個人并且最后都要過河,但是,河兩岸隨時都要美國人數少與英國人,否則英國人會被殺死,這六個人如何才能安全渡過去? 1.過去2個美國人;2.過去1個英國人;3.回來2個美國人;4.過去2個英國人;5.過去2個美國人;6.過去1個美國人
總結
以上是生活随笔 為你收集整理的Java总结及面试题 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。