mysql用大白话解释_Java基础--2021Java面试题系列教程--大白话解读
前言
序言
再高大上的框架,也需要扎實(shí)的基礎(chǔ)才能玩轉(zhuǎn),高頻面試問題更是基礎(chǔ)中的高頻實(shí)戰(zhàn)要點(diǎn)。
適合閱讀人群
Java 學(xué)習(xí)者和愛好者,有一定工作經(jīng)驗(yàn)的技術(shù)人,準(zhǔn)面試官等。
閱讀建議
本教程是系列教程,包含 Java 基礎(chǔ),JVM,容器,多線程,反射,異常,網(wǎng)絡(luò),對(duì)象拷貝,JavaWeb,設(shè)計(jì)模式,Spring-Spring MVC,Spring Boot / Spring Cloud,Mybatis / Hibernate,Kafka,RocketMQ,Zookeeper,MySQL,Redis,Elasticsearch,Lucene
微信搜:JavaPub,閱讀全套系列面試題教程
[toc]
題目
1.JDK 和 JRE 有什么區(qū)別?談?wù)勀銓?duì) JVM 的理解
JDK 和 JRE
JDK(Java Development Kit)是Java開發(fā)運(yùn)行環(huán)境,是java開發(fā)工具包,JDK包含了JRE的所有東西,同時(shí)還包含了編譯java源碼的編譯器javac,還包含了很多java程序調(diào)試和分析的工具:jconsole,jvisualvm等工具軟件,還包含了java程序編寫所需的文檔和demo例子程序。
JRE(Java Runtime Environment)它是Java運(yùn)行環(huán)境,如果你不需要開發(fā)只需要運(yùn)行Java程序,那么你可以安裝JRE。(但是在運(yùn)行JSP程序時(shí),我們還是需要JDK,因?yàn)閼?yīng)用服務(wù)器會(huì)將 JSP 轉(zhuǎn)換為 Java servlet,并且需要使用 JDK 編譯 servlet。)
如果你需要運(yùn)行java程序,只需安裝JRE就可以了。如果你需要編寫java程序,需要安裝JDK。
JVM
JVM(Java Virtual Machine) 就是我們常說的 java 虛擬機(jī)是 JRE 的一部分,它是整個(gè) java 實(shí)現(xiàn)跨平臺(tái)的最核心的部分,所有的 java 程序會(huì)首先被編譯為 .class 的類文件,這種類文件可以在虛擬機(jī)上執(zhí)行。
JVM 主要工作是解釋自己的指令集(即字節(jié)碼)并映射到本地的 CPU 指令集和 OS 的系統(tǒng)調(diào)用。Java 語言是跨平臺(tái)運(yùn)行的,不同的操作系統(tǒng)會(huì)有不同的 JVM 映射規(guī)則,使之與操作系統(tǒng)無關(guān),完成跨平臺(tái)性。
JVM是Java Virtual Machine(Java虛擬機(jī))的縮寫,是通過在實(shí)際的計(jì)算機(jī)上仿真模擬各種計(jì)算機(jī)功能來實(shí)現(xiàn)的。由一套字節(jié)碼指令集、一組寄存器、一個(gè)棧、一個(gè)垃圾回收堆和一個(gè)存儲(chǔ)方法域等組成。JVM屏蔽了與操作系統(tǒng)平臺(tái)相關(guān)的信息,使得Java程序只需要生成在Java虛擬機(jī)上運(yùn)行的目標(biāo)代碼(字節(jié)碼),就可在多種平臺(tái)上不加修改的運(yùn)行,這也是Java能夠“一次編譯,到處運(yùn)行的”原因。
附一張關(guān)系圖:
總結(jié):使用JDK(調(diào)用JAVA API)開發(fā)JAVA程序后,通過JDK中的編譯程序(javac)將Java程序編譯為Java字節(jié)碼,在JRE上運(yùn)行這些字節(jié)碼,JVM會(huì)解析并映射到真實(shí)操作系統(tǒng)的CPU指令集和OS的系統(tǒng)調(diào)用。
2.== 和 equals 的區(qū)別是什么?
equals 和 == 都是來判斷兩個(gè)對(duì)象是否相等
equals 是方法,而 == 是操作符;
對(duì)于基本類型的變量來說(如 short、 int、 long、 float、 double),只能使用 == ,因?yàn)檫@些基本類型的變量沒有 equals 方法。對(duì)于基本類型變量的比較,使用 == 比較, 一般比較的是它們的值。
對(duì)于引用類型的變量來說(例如 String 類)才有 equals 方法,因?yàn)?String 繼承了 Object 類, equals 是 Object 類的通用方法。對(duì)于該類型對(duì)象的比較,默認(rèn)情況下,也就是沒有復(fù)寫 Object 類的 equals 方法,使用 == 和 equals 比較是一樣效果的,都是比較的是它們?cè)趦?nèi)存中的存放地址。但是對(duì)于某些類來說,為了滿足自身業(yè)務(wù)需求,可能存在 equals 方法被復(fù)寫的情況,這時(shí)使用 equals 方法比較需要看具體的情況,例如 String 類,使用 equals 方法會(huì)比較它們的值;
對(duì)于 equals 方法沒有被重寫的情況。如果類沒有重寫該方法,那么默認(rèn)使用的就是 Object 類的方法,以下是 Object 類的 equals 方法:
public boolean equals(Object obj) {
return (this == obj);
}
從源碼可以看出,里面使用的就是 == 比較,所以這種情況下比較的就是它們?cè)趦?nèi)存中的存放地址。
對(duì)于 equals 方法被重寫的情況。以 String 類為例,以下是 String 類中的 equals 方法:
@Override
public boolean equals(Object other) {
if (other == this) {
return true;
}
if (other instanceof String) {
String s = (String)other;
int count = this.count;
if (s.count != count) {
return false;
}
if (hashCode() != s.hashCode()) {
return false;
}
char[] value1 = value;
int offset1 = offset;
char[] value2 = s.value;
int offset2 = s.offset;
for (int end = offset1 + count; offset1 < end; ) {
if (value1[offset1] != value2[offset2]) {
return false;
}
offset1++;
offset2++;
}
return true;
} else {
return false;
}
}
從源碼可以看出, String 類復(fù)寫了 equals 方法,當(dāng)使用 == 比較內(nèi)存的存放地址不相等時(shí),接下來會(huì)比較字符串的內(nèi)容是否 相等,所以 String 類中的 equals 方法會(huì)比較兩者的字符串內(nèi)容是否一樣。
3.兩個(gè)對(duì)象的 hashCode()相同,則 equals()也一定為 true,對(duì)嗎?
答案是不一定的
java.lang.Object類中有兩個(gè)非常重要的方法:
public boolean equals(Object obj)
public int hashCode()
Object 類是類繼承結(jié)構(gòu)的基礎(chǔ),所以是每一個(gè)類的父類。所有的對(duì)象,包括數(shù)組,都實(shí)現(xiàn)了在 Object 類中定義的方法。
以下是Object對(duì)象API關(guān)于equal方法和hashCode方法的說明:
If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.
It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hash tables.
簡(jiǎn)而言之,在集合查找時(shí),hashcode能大大降低對(duì)象比較次數(shù),提高查找效率!
Java對(duì)象的eqauls方法和hashCode方法是這樣規(guī)定的:
相等(相同)的對(duì)象必須具有相等的哈希碼(或者散列碼)。
如果兩個(gè)對(duì)象的hashCode相同,它們并不一定相同。
對(duì)以上倆點(diǎn)的說明
關(guān)于第一點(diǎn),相等(相同)的對(duì)象必須具有相等的哈希碼(或者散列碼),為什么?
想象一下,假如兩個(gè)Java對(duì)象A和B,A和B相等(eqauls結(jié)果為true),但A和B的哈希碼不同,則A和B存入HashMap時(shí)的哈希碼計(jì)算得到的HashMap內(nèi)部數(shù)組位置索引可能不同,那么A和B很有可能允許同時(shí)存入HashMap,顯然相等/相同的元素是不允許同時(shí)存入HashMap,HashMap不允許存放重復(fù)元素。
關(guān)于第二點(diǎn),兩個(gè)對(duì)象的hashCode相同,它們并不一定相同
也就是說,不同對(duì)象的hashCode可能相同;假如兩個(gè)Java對(duì)象A和B,A和B不相等(eqauls結(jié)果為false),但A和B的哈希碼相等,將A和B都存入HashMap時(shí)會(huì)發(fā)生哈希沖突,也就是A和B存放在HashMap內(nèi)部數(shù)組的位置索引相同這時(shí)HashMap會(huì)在該位置建立一個(gè)鏈接表,將A和B串起來放在該位置,顯然,該情況不違反HashMap的使用原則,是允許的。當(dāng)然,哈希沖突越少越好,盡量采用好的哈希算法以避免哈希沖突。
總而言之(all in all):
換句話說,equals()方法不相等的兩個(gè)對(duì)象,hashcode()有可能相等(我的理解是由于哈希碼在生成的時(shí)候產(chǎn)生沖突造成的)。反過來,hashcode()不等,一定能推出equals()也不等;hashcode()相等,equals()可能相等,也可能不等。
4.final 在 java 中有什么作用?
這是一個(gè)很基礎(chǔ),很能體現(xiàn)你基礎(chǔ)是否扎實(shí)、是否有鉆研精神的知識(shí)點(diǎn)。
> final關(guān)鍵字的字面意思是最終的, 不可修改的. 這似乎是一個(gè)看見名字就大概能知道怎么用的語法。
這三點(diǎn)必須要答出來:
被final修飾的類不可以被繼承
被final修飾的方法不可以被重寫
被final修飾的變量不可以被改變
要注意final類中的所有成員方法都會(huì)被隱式地指定為final方法。
第三點(diǎn)尤為重要
當(dāng)final修飾的是一個(gè)基本數(shù)據(jù)類型數(shù)據(jù)時(shí), 這個(gè)數(shù)據(jù)的值在初始化后將不能被改變; 當(dāng)final修飾的是一個(gè)引用類型數(shù)據(jù)時(shí), 也就是修飾一個(gè)對(duì)象時(shí), 引用在初始化后將永遠(yuǎn)指向一個(gè)內(nèi)存地址, 不可修改. 但是該內(nèi)存地址中保存的對(duì)象信息, 是可以進(jìn)行修改的.
5.java 中的 Math.round(-1.5) 等于多少?
返回值:-1
四舍五入的原理是在參數(shù)上加0.5然后做向下取整。
一些案例:
public class test {
public static void main(String[] args){
System.out.println(Math.round(1.3)); //1
System.out.println(Math.round(1.4)); //1
System.out.println(Math.round(1.5)); //2
System.out.println(Math.round(1.6)); //2
System.out.println(Math.round(1.7)); //2
System.out.println(Math.round(-1.3)); //-1
System.out.println(Math.round(-1.4)); //-1
System.out.println(Math.round(-1.5)); //-1
System.out.println(Math.round(-1.6)); //-2
System.out.println(Math.round(-1.7)); //-2
}
}
6.String 屬于基礎(chǔ)的數(shù)據(jù)類型嗎?
當(dāng)然,每一個(gè)Java學(xué)習(xí)者都知道它不是基礎(chǔ)類型,但是你要知道更多細(xì)節(jié)。
Java中的數(shù)據(jù)類型分為兩大類,基本數(shù)據(jù)類型和引用數(shù)據(jù)類型。
基本數(shù)據(jù)類型只有8種,可按照如下分類
整數(shù)類型:long、int、short、byte
浮點(diǎn)類型:float、double
字符類型:char
布爾類型:boolean
引用數(shù)據(jù)類型非常多,大致包括:
類、 接口類型、 數(shù)組類型、 枚舉類型、 注解類型、 字符串型。
> 簡(jiǎn)單來說,所有的非基本數(shù)據(jù)類型都是引用數(shù)據(jù)類型。
基本數(shù)據(jù)類型和引用數(shù)據(jù)類型的區(qū)別
存儲(chǔ)位置
基本變量類型
> 在方法中定義的非全局基本數(shù)據(jù)類型變量的具體內(nèi)容是存儲(chǔ)在棧中的
引用變量類型
> 只要是引用數(shù)據(jù)類型變量,其具體內(nèi)容都是存放在堆中的,而棧中存放的是其具體內(nèi)容所在內(nèi)存的地址(引用/句柄)
ps:通過變量地址可以找到變量的具體內(nèi)容,就如同通過房間號(hào)可以找到房間一般
傳遞方式
基本變量類型
> 在方法中定義的非全局基本數(shù)據(jù)類型變量,調(diào)用方法時(shí)作為參數(shù)是按數(shù)值傳遞的
//基本數(shù)據(jù)類型作為方法參數(shù)被調(diào)用
public class Main{
public static void main(String[] args){
int msg = 100;
System.out.println("調(diào)用方法前msg的值:\n"+ msg); //100
fun(msg);
System.out.println("調(diào)用方法后msg的值:\n"+ msg); //100
}
public static void fun(int temp){
temp = 0;
}
}
引用變量類型
> 引用數(shù)據(jù)類型變量,調(diào)用方法時(shí)作為參數(shù)是按引用傳遞的
//引用數(shù)據(jù)類型作為方法參數(shù)被調(diào)用
class Book{
String name;
double price;
public Book(String name,double price){
this.name = name;
this.price = price;
}
public void getInfo(){
System.out.println("圖書名稱:"+ name + ",價(jià)格:" + price);
}
public void setPrice(double price){
this.price = price;
}
}
public class Main{
public static void main(String[] args){
Book book = new Book("Java開發(fā)指南",66.6);
book.getInfo(); //圖書名稱:Java開發(fā)指南,價(jià)格:66.6
fun(book);
book.getInfo(); //圖書名稱:Java開發(fā)指南,價(jià)格:99.9
}
public static void fun(Book temp){
temp.setPrice(99.9);
}
}
> 調(diào)用時(shí)為temp在棧中開辟新空間,并指向book的具體內(nèi)容,方法執(zhí)行完畢后temp在棧中的內(nèi)存被釋放掉
其他(有關(guān)JVM)
java 中String 是個(gè)對(duì)象,是引用類型 ,基礎(chǔ)類型與引用類型的區(qū)別是,基礎(chǔ)類型只表示簡(jiǎn)單的字符或數(shù)字,引用類型可以是任何復(fù)雜的數(shù)據(jù)結(jié)構(gòu),基本類型僅表示簡(jiǎn)單的數(shù)據(jù)類型,引用類型可以表示復(fù)雜的數(shù)據(jù)類型,還可以操作這種數(shù)據(jù)類型的行為 。
java虛擬機(jī)處理基礎(chǔ)類型與引用類型的方式是不一樣的,對(duì)于基本類型,java虛擬機(jī)會(huì)為其分配數(shù)據(jù)類型實(shí)際占用的內(nèi)存空間,而對(duì)于引用類型變量,他僅僅是一個(gè)指向堆區(qū)中某個(gè)實(shí)例的指針。
基本類型存儲(chǔ)在棧中,因此它們的存取速度要快于存儲(chǔ)在堆中的對(duì)應(yīng)包裝類的實(shí)例對(duì)象,從Java5.0(1.5)開始,JAVA虛擬機(jī)(Java Virtual Machine)可以完成基本類型和它們對(duì)應(yīng)包裝類之間的自動(dòng)轉(zhuǎn)換。因此我們?cè)谫x值、參數(shù)傳遞以及數(shù)學(xué)運(yùn)算的時(shí)候像使用基本類型一樣使用它們的包裝類,但這并不意味著你可以通過基本類型調(diào)用它們的包裝類才具有的方法。另外,所有基本類型(包括void)的包裝類都使用了final修飾,因此我們無法繼承它們擴(kuò)展新的類,也無法重寫它們的任何方法。
基本類型的優(yōu)勢(shì):數(shù)據(jù)存儲(chǔ)相對(duì)簡(jiǎn)單,運(yùn)算效率比較高。
包裝類的優(yōu)勢(shì):自帶方法豐富,集合的元素必須是對(duì)象類型,體現(xiàn)了Java一切皆是對(duì)象的思想。
7.java 中操作字符串都有哪些類?它們之間有什么區(qū)別?
String、StringBuffer、StringBuilder
String : final修飾,String類的方法都是返回new String。即對(duì)String對(duì)象的任何改變都不影響到原對(duì)象,對(duì)字符串的修改操作都會(huì)生成新的對(duì)象。
StringBuffer : 對(duì)字符串的操作的方法都加了synchronized,保證線程安全。
StringBuilder : 不保證線程安全,在方法體內(nèi)需要進(jìn)行字符串的修改操作,可以new StringBuilder對(duì)象,調(diào)用StringBuilder對(duì)象的append、replace、delete等方法修改字符串。
8.String str="i"與 String str=new String(“i”)一樣嗎?
答:不一樣。
因?yàn)閮?nèi)存的分配方式不一樣。String str="i"的方式,Java 虛擬機(jī)會(huì)將其分配到常量池中;而 String str=new String(“i”)方式,則會(huì)被分到堆內(nèi)存中。
String str1 = "i";
String str2 = "i";
String str3 = new String("i");
System.out.println(str1 == str2);//ture
System.out.println(str2 == str3);//false
解釋:
Java 虛擬機(jī)會(huì)將其分配到常量池中:常量池不會(huì)重復(fù)創(chuàng)建對(duì)象。
> 在String str1="i"中,把i值存在常量池,地址賦給str1。假設(shè)再寫一個(gè)String str2="i",則會(huì)把i的地址賦給str2,但是i對(duì)象不會(huì)重新創(chuàng)建,他們引用的是同一個(gè)地址值,共享同一個(gè)i內(nèi)存。(需要注意的是:String str="i"; 因?yàn)镾tring 是final類型的,所以“i”應(yīng)該是在常量池。)
分到堆內(nèi)存中:堆內(nèi)存會(huì)創(chuàng)建新的對(duì)象。
> 假設(shè)再寫一個(gè)String str3=new String(“i”),則會(huì)創(chuàng)建一個(gè)新的i對(duì)象,然后將新對(duì)象的地址值賦給str3。雖然str3和str1的值相同但是地址值不同。(而new String("i");則是新建對(duì)象放到堆內(nèi)存中。)
拓展知識(shí):
堆內(nèi)存用來存放由new創(chuàng)建的對(duì)象和數(shù)組。在堆中分配的內(nèi)存,由Java虛擬機(jī)的自動(dòng)垃圾回收器來管理。
常量池指的是在編譯期被確定,并被保存在已編譯的.class文件中的一些數(shù)據(jù)。
== :引用數(shù)據(jù)類型比較地址值;
equals:引用類型,重寫前比較兩個(gè)對(duì)象地址值,重寫后比較屬性值。
9.如何將字符串反轉(zhuǎn)?
倆種辦法:
使用 StringBuilder 或 StringBuffer 的 reverse 方法,本質(zhì)都調(diào)用了它們的父類 AbstractStringBuilder 的 reverse 方法實(shí)現(xiàn)。(JDK1.8)
不考慮字符串中的字符是否是 Unicode 編碼,自己實(shí)現(xiàn)。
public static void main(String[] args) {
String str = "ABCDE";
System.out.println(reverseStringByStringBuilderApi(str));
System.out.println(reverseString(str));
}
/**
* 和StringBuffer()一樣,都用了Java自實(shí)現(xiàn)的方法,使用位移來實(shí)現(xiàn)
* @param
* @return
*/
public static String reverseStringByStringBuilderApi(String str) {
if (str != null && str.length() > 0) {
return new StringBuilder(str).reverse().toString();
}
return str;
}
public static String reverseString(String str) {
if (str != null && str.length() > 0) {
int len = str.length();
char[] chars = new char[len];
for (int i = len - 1; i >= 0; i--) {
chars[len - 1 - i] = str.charAt(i);
}
return new String(chars);
}
return str;
}
10.String 類的常用方法都有那些?
下面列舉了20個(gè)常用方法。格式:返回類型 方法名 作用。
和長(zhǎng)度有關(guān):
int length() 得到一個(gè)字符串的字符個(gè)數(shù)
和數(shù)組有關(guān):
byte[] getByte() ) 將一個(gè)字符串轉(zhuǎn)換成字節(jié)數(shù)組
char[] toCharArray() 將一個(gè)字符串轉(zhuǎn)換成字符數(shù)組
String split(String) 將一個(gè)字符串按照指定內(nèi)容劈開
和判斷有關(guān):
boolean equals() 判斷兩個(gè)字符串的內(nèi)容是否一樣
boolean equalsIsIgnoreCase(String) 忽略太小寫的比較兩個(gè)字符串的內(nèi)容是否一樣
boolean contains(String) 判斷一個(gè)字符串里面是否包含指定的內(nèi)容
boolean startsWith(String) 判斷一個(gè)字符串是否以指定的內(nèi)容開頭
boolean endsWith(String) 判斷一個(gè)字符串是否以指定的內(nèi)容結(jié)尾
和改變內(nèi)容有關(guān):
String toUpperCase() 將一個(gè)字符串全部轉(zhuǎn)換成大寫
String toLowerCase() 將一個(gè)字符串全部轉(zhuǎn)換成小寫
String replace(String,String) 將某個(gè)內(nèi)容全部替換成指定內(nèi)容
String replaceAll(String,String) 將某個(gè)內(nèi)容全部替換成指定內(nèi)容,支持正則
String repalceFirst(String,String) 將第一次出現(xiàn)的某個(gè)內(nèi)容替換成指定的內(nèi)容
String substring(int) 從指定下標(biāo)開始一直截取到字符串的最后
String substring(int,int) 從下標(biāo)x截取到下標(biāo)y-1對(duì)應(yīng)的元素
String trim() 去除一個(gè)字符串的前后空格
和位置有關(guān):
char charAt(int) 得到指定下標(biāo)位置對(duì)應(yīng)的字符
int indexOf(String) 得到指定內(nèi)容第一次出現(xiàn)的下標(biāo)
int lastIndexOf(String) 得到指定內(nèi)容最后一次出現(xiàn)的下標(biāo)
11.抽象類必須要有抽象方法嗎?
答案是:不必須
這道題考察的是抽象類的知識(shí):
抽象類必須有關(guān)鍵字 abstract 來修飾。
抽象類可以不含有抽象方法
如果一個(gè)類包含抽象方法,則該類必須是抽象類
抽象類的特性和使用:
抽象類不能被實(shí)例化。因?yàn)槌橄箢愔蟹椒ㄎ淳唧w化,這是一種不完整的類,所以直接實(shí)例化也就沒有意義了。
抽象類的使用必須有子類,使用extends繼承,一個(gè)子類只能繼承一個(gè)抽象類。
子類(如果不是抽象類)則必須覆寫抽象類之中的全部抽象方法(如果子類沒有實(shí)現(xiàn)父類的抽象方法,則必須將子類也定義為為abstract類。)。
抽象類可以不包含抽象方法,但如果類中包含抽象方法,就必須將該類聲明為抽象類。
抽象類的基本使用示例:
//定義一個(gè)抽象類
abstract class A{
//普通方法
public void fun(){
System.out.println("存在方法體的方法");
}
//抽象方法,沒有方法體,有abstract關(guān)鍵字做修飾
public abstract void print();
}
//單繼承
//B類是抽象類的子類,是一個(gè)普通類
class B extends A{
//強(qiáng)制要求覆寫
@Override
public void print() {
System.out.println("Hello World !");
}
}
public class TestDemo {
public static void main(String[] args) {
//向上轉(zhuǎn)型
A a = new B();
//被子類所覆寫的過的方法
a.print();
}
}
12.普通類和抽象類有哪些區(qū)別?
包含抽象方法的類稱為抽象類,但并不意味著抽象類中只能有抽象方法,它和普通類一樣,同樣可以擁有成員變量和普通的成員方法。注意,抽象類和普通類的主要有三點(diǎn)區(qū)別:
抽象方法必須為public或者protected(因?yàn)槿绻麨閜rivate,則不能被子類繼承,子類便無法實(shí)現(xiàn)該方法),缺省情況下默認(rèn)為public。
抽象類不能用來創(chuàng)建對(duì)象;
如果一個(gè)類繼承于一個(gè)抽象類,則子類必須實(shí)現(xiàn)父類的抽象方法。如果子類沒有實(shí)現(xiàn)父類的抽象方法,則必須將子類也定義為為abstract類。
13.抽象類能使用 final 修飾嗎?
不能,抽象類是被用于繼承的,final修飾代表不可修改、不可繼承的。
這個(gè)在前面幾題有過介紹。
14.接口和抽象類有什么區(qū)別?
語法層面上的區(qū)別,也是我們?nèi)粘9俜降囊恍┱f法:
抽象類可以提供成員方法的實(shí)現(xiàn)細(xì)節(jié),而接口中只能存在public abstract 方法;
抽象類中的成員變量可以是各種類型的,而接口中的成員變量只能是public static final類型的;
接口中不能含有靜態(tài)代碼塊以及靜態(tài)方法,而抽象類可以有靜態(tài)代碼塊和靜態(tài)方法;
一個(gè)類只能繼承一個(gè)抽象類,而一個(gè)類卻可以實(shí)現(xiàn)多個(gè)接口。
> 接口的設(shè)計(jì)目的,是對(duì)類的行為進(jìn)行約束。而抽象類的設(shè)計(jì)目的,是代碼復(fù)用。總結(jié)來說,繼承是一個(gè) "是不是"的關(guān)系,而 接口 實(shí)現(xiàn)則是 "有沒有"的關(guān)系。如果一個(gè)類繼承了某個(gè)抽象類,則子類必定是抽象類的種類,而接口實(shí)現(xiàn)則是有沒有、具備不具備的關(guān)系。
抽象和繼承是 Java 中非常重要的東西,深入理解可以讓我們對(duì) Java 技術(shù)理解更深刻,參考的這篇知乎博文非常好。
15.java 中 IO 流分為幾種?
Java中的流分為兩種,一種是字節(jié)流,另一種是字符流,分別由四個(gè)抽象類來表示(每種流包括輸入和輸出兩種所以一共四個(gè)):InputStream,OutputStream,Reader,Writer。Java中其他多種多樣變化的流均是由它們派生出來的.
字符流和字節(jié)流是根據(jù)處理數(shù)據(jù)的不同來區(qū)分的。字節(jié)流按照8位傳輸,字節(jié)流是最基本的,所有文件的儲(chǔ)存是都是字節(jié)(byte)的儲(chǔ)存,在磁盤上保留的并不是文件的字符而是先把字符編碼成字節(jié),再儲(chǔ)存這些字節(jié)到磁盤。
字節(jié)流可用于任何類型的對(duì)象,包括二進(jìn)制對(duì)象,而字符流只能處理字符或者字符串;
節(jié)流提供了處理任何類型的IO操作的功能,但它不能直接處理Unicode字符,而字符流就可以。
讀文本的時(shí)候用字符流,例如txt文件。讀非文本文件的時(shí)候用字節(jié)流,例如mp3。理論上任何文件都能夠用字節(jié)流讀取,但當(dāng)讀取的是文本數(shù)據(jù)時(shí),為了能還原成文本你必須再經(jīng)過一個(gè)轉(zhuǎn)換的工序,相對(duì)來說字符流就省了這個(gè)麻煩,可以有方法直接讀取。
字符流處理的單元為2個(gè)字節(jié)的Unicode字符,分別操作字符、字符數(shù)組或字符串,而字節(jié)流處理單元為1個(gè)字節(jié), 操作字節(jié)和字節(jié)數(shù)組。所以字符流是由Java虛擬機(jī)將字節(jié)轉(zhuǎn)化為2個(gè)字節(jié)的Unicode字符為單位的字符而成的,所以它對(duì)多國語言支持性比較好!
按操作方式分類結(jié)構(gòu)圖:
按操作對(duì)象分類結(jié)構(gòu)圖:
16.BIO、NIO、AIO 有什么區(qū)別?
BIO
BIO全稱是Blocking IO,是JDK1.4之前的傳統(tǒng)IO模型,本身是同步阻塞模式。 線程發(fā)起IO請(qǐng)求后,一直阻塞IO,直到緩沖區(qū)數(shù)據(jù)就緒后,再進(jìn)入下一步操作。針對(duì)網(wǎng)絡(luò)通信都是一請(qǐng)求一應(yīng)答的方式,雖然簡(jiǎn)化了上層的應(yīng)用開發(fā),但在性能和可靠性方面存在著巨大瓶頸,試想一下如果每個(gè)請(qǐng)求都需要新建一個(gè)線程來專門處理,那么在高并發(fā)的場(chǎng)景下,機(jī)器資源很快就會(huì)被耗盡。
NIO
NIO也叫Non-Blocking IO 是同步非阻塞的IO模型。線程發(fā)起io請(qǐng)求后,立即返回(非阻塞io)。同步指的是必須等待IO緩沖區(qū)內(nèi)的數(shù)據(jù)就緒,而非阻塞指的是,用戶線程不原地等待IO緩沖區(qū),可以先做一些其他操作,但是要定時(shí)輪詢檢查IO緩沖區(qū)數(shù)據(jù)是否就緒。Java中的NIO 是new IO的意思。其實(shí)是NIO加上IO多路復(fù)用技術(shù)。普通的NIO是線程輪詢查看一個(gè)IO緩沖區(qū)是否就緒,而Java中的new IO指的是線程輪詢地去查看一堆IO緩沖區(qū)中哪些就緒,這是一種IO多路復(fù)用的思想。IO多路復(fù)用模型中,將檢查IO數(shù)據(jù)是否就緒的任務(wù),交給系統(tǒng)級(jí)別的select或epoll模型,由系統(tǒng)進(jìn)行監(jiān)控,減輕用戶線程負(fù)擔(dān)。 NIO主要有buffer、channel、selector三種技術(shù)的整合,通過零拷貝的buffer取得數(shù)據(jù),每一個(gè)客戶端通過channel在selector(多路復(fù)用器)上進(jìn)行注冊(cè)。服務(wù)端不斷輪詢channel來獲取客戶端的信息。channel上有connect,accept(阻塞)、read(可讀)、write(可寫)四種狀態(tài)標(biāo)識(shí)。根據(jù)標(biāo)識(shí)來進(jìn)行后續(xù)操作。所以一個(gè)服務(wù)端可接收無限多的channel。不需要新開一個(gè)線程。大大提升了性能。
AIO
AIO是真正意義上的異步非阻塞IO模型。 上述NIO實(shí)現(xiàn)中,需要用戶線程定時(shí)輪詢,去檢查IO緩沖區(qū)數(shù)據(jù)是否就緒,占用應(yīng)用程序線程資源,其實(shí)輪詢相當(dāng)于還是阻塞的,并非真正解放當(dāng)前線程,因?yàn)樗€是需要去查詢哪些IO就緒。而真正的理想的異步非阻塞IO應(yīng)該讓內(nèi)核系統(tǒng)完成,用戶線程只需要告訴內(nèi)核,當(dāng)緩沖區(qū)就緒后,通知我或者執(zhí)行我交給你的回調(diào)函數(shù)。 AIO可以做到真正的異步的操作,但實(shí)現(xiàn)起來比較復(fù)雜,支持純異步IO的操作系統(tǒng)非常少,目前也就windows是IOCP技術(shù)實(shí)現(xiàn)了,而在Linux上,底層還是是使用的epoll實(shí)現(xiàn)的。
資料:
> BIO (Blocking I/O): 同步阻塞I/O模式,數(shù)據(jù)的讀取寫入必須阻塞在一個(gè)線程內(nèi)等待其完成。在活動(dòng)連接數(shù)不是特別高(小于單機(jī)1000)的情況下,這種模型是比較不錯(cuò)的,可以讓每一個(gè)連接專注于自己的 I/O 并且編程模型簡(jiǎn)單,也不用過多考慮系統(tǒng)的過載、限流等問題。線程池本身就是一個(gè)天然的漏斗,可以緩沖一些系統(tǒng)處理不了的連接或請(qǐng)求。但是,當(dāng)面對(duì)十萬甚至百萬級(jí)連接的時(shí)候,傳統(tǒng)的 BIO 模型是無能為力的。因此,我們需要一種更高效的 I/O 處理模型來應(yīng)對(duì)更高的并發(fā)量。
> NIO (New I/O): NIO是一種同步非阻塞的I/O模型,在Java 1.4 中引入了NIO框架,對(duì)應(yīng) java.nio 包,提供了 Channel , Selector,Buffer等抽象。NIO中的N可以理解為Non-blocking,不單純是New。它支持面向緩沖的,基于通道的I/O操作方法。 NIO提供了與傳統(tǒng)BIO模型中的 Socket 和 ServerSocket 相對(duì)應(yīng)的 SocketChannel 和 ServerSocketChannel 兩種不同的套接字通道實(shí)現(xiàn),兩種通道都支持阻塞和非阻塞兩種模式。阻塞模式使用就像傳統(tǒng)中的支持一樣,比較簡(jiǎn)單,但是性能和可靠性都不好;非阻塞模式正好與之相反。對(duì)于低負(fù)載、低并發(fā)的應(yīng)用程序,可以使用同步阻塞I/O來提升開發(fā)速率和更好的維護(hù)性;對(duì)于高負(fù)載、高并發(fā)的(網(wǎng)絡(luò))應(yīng)用,應(yīng)使用 NIO 的非阻塞模式來開發(fā)
> AIO (Asynchronous I/O): AIO 也就是 NIO 2。在 Java 7 中引入了 NIO 的改進(jìn)版 NIO 2,它是異步非阻塞的IO模型。異步 IO 是基于事件和回調(diào)機(jī)制實(shí)現(xiàn)的,也就是應(yīng)用操作之后會(huì)直接返回,不會(huì)堵塞在那里,當(dāng)后臺(tái)處理完成,操作系統(tǒng)會(huì)通知相應(yīng)的線程進(jìn)行后續(xù)的操作。AIO 是異步IO的縮寫,雖然 NIO 在網(wǎng)絡(luò)操作中,提供了非阻塞的方法,但是 NIO 的 IO 行為還是同步的。對(duì)于 NIO 來說,我們的業(yè)務(wù)線程是在 IO 操作準(zhǔn)備好時(shí),得到通知,接著就由這個(gè)線程自行進(jìn)行 IO 操作,IO操作本身是同步的。查閱網(wǎng)上相關(guān)資料,我發(fā)現(xiàn)就目前來說 AIO 的應(yīng)用還不是很廣泛,Netty 之前也嘗試使用過 AIO,不過又放棄了。
17.Files的常用方法都有哪些?
Files.exists():檢測(cè)文件路徑是否存在。
Files.createFile():創(chuàng)建文件。
Files.createDirectory():創(chuàng)建文件夾。
Files.delete():刪除一個(gè)文件或目錄。
Files.copy():復(fù)制文件。
Files.move():移動(dòng)文件。
Files.size():查看文件個(gè)數(shù)。
Files.read():讀取文件。
Files.write():寫入文件。
微信關(guān)注:JavaPub ,帶走全套寶典
總結(jié)
以上是生活随笔為你收集整理的mysql用大白话解释_Java基础--2021Java面试题系列教程--大白话解读的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: mysql存储过程返回多个值_数据库my
 - 下一篇: mdb批量导入mysql_快速将 acc