本溪做網(wǎng)站制作網(wǎng)頁完整步驟
一、面向?qū)ο蠡A(chǔ)
1. 面向?qū)ο蠛兔嫦蜻^程的區(qū)別
- 面向過程把解決問題的過程拆成一個(gè)個(gè)方法,通過一個(gè)個(gè)方法的執(zhí)行解決問題。
- 面向?qū)ο髸?mark>先抽象出對象,然后用對象執(zhí)行方法的方式解決問題。
2.創(chuàng)建一個(gè)對象用什么運(yùn)算符?對象實(shí)體與對象引用有何不同?
new 運(yùn)算符,new 創(chuàng)建對象實(shí)例(對象實(shí)例在堆內(nèi)存中),對象引用指向?qū)ο髮?shí)例(對象引用存放在棧內(nèi)存中)。
- 一個(gè)對象引用可以指向 0 個(gè)或 1 個(gè)對象
- 一個(gè)對象可以有 n 個(gè)引用指向它
3.對象的相等和引用相等的區(qū)別
- 對象的相等一般比較的是內(nèi)存中存放的內(nèi)容是否相等。
- 引用相等一般比較的是他們指向的內(nèi)存地址是否相等。
4.如果一個(gè)類沒有聲明構(gòu)造方法,該程序能正確執(zhí)行嗎?
- 構(gòu)造方法是一種特殊的方法,主要作用是完成對象的初始化工作。
- 如果一個(gè)類沒有聲明構(gòu)造方法,也可以執(zhí)行!因?yàn)橐粋€(gè)類即使沒有聲明構(gòu)造方法也會有默認(rèn)的不帶參數(shù)的構(gòu)造方法。
- 如果我們自己添加了類的構(gòu)造方法(無論是否有參),Java 就不會添加默認(rèn)的無參數(shù)的構(gòu)造方法了
5.構(gòu)造方法有哪些特點(diǎn)?是否可被 override?
構(gòu)造方法特點(diǎn)如下:
- 名字與類名相同。
- 沒有返回值,但不能用 void 聲明構(gòu)造函數(shù)。
- 生成類的對象時(shí)自動執(zhí)行,無需調(diào)用。
構(gòu)造方法不能被 override(重寫),但是可以 overload(重載
6.面向?qū)ο笕筇卣?/h3>
6.1封裝
封裝是指把一個(gè)對象的狀態(tài)信息(也就是屬性)隱藏在對象內(nèi)部,不允許外部對象直接訪問對象的內(nèi)部信息。
public class Student {private int id;//id屬性私有化private String name;//name屬性私有化//獲取id的方法public int getId() {return id;}//設(shè)置id的方法public void setId(int id) {this.id = id;}//獲取name的方法public String getName() {return name;}//設(shè)置name的方法public void setName(String name) {this.name = name;}
}
6.2繼承
不同類型的對象,相互之間經(jīng)常有一定數(shù)量的共同點(diǎn)
通過使用繼承,可以快速地創(chuàng)建新的類,可以提高代碼的重用,程序的可維護(hù)性,節(jié)省大量創(chuàng)建新類的時(shí)間 ,提高我們的開發(fā)效率。
關(guān)于繼承如下 3 點(diǎn)請記住:
- 子類擁有父類對象所有的屬性和方法(包括私有屬性和私有方法),但是父類中的私有屬性和方法子類是無法訪問,只是擁有。
- 子類可以擁有自己屬性和方法,即子類可以對父類進(jìn)行擴(kuò)展。
- 子類可以用自己的方式實(shí)現(xiàn)父類的方法。
6.3多態(tài)
表示一個(gè)對象具有多種的狀態(tài),具體表現(xiàn)為父類的引用指向子類的實(shí)例
多態(tài)的特點(diǎn):
- 對象類型和引用類型之間具有繼承(類)/實(shí)現(xiàn)(接口)的關(guān)系;
- 引用類型變量發(fā)出的方法調(diào)用的到底是哪個(gè)類中的方法,必須在程序運(yùn)行期間才能確定;
- 多態(tài)不能調(diào)用“只在子類存在但在父類不存在”的方法;
- 如果子類重寫了父類的方法,真正執(zhí)行的是子類覆蓋的方法,如果子類沒有覆蓋父類的方法,執(zhí)行的是父類的方法
7.接口和抽象類有什么共同點(diǎn)和區(qū)別?
共同點(diǎn):
- 都不能被實(shí)例化。
- 都可以包含抽象方法。
- 都可以有默認(rèn)實(shí)現(xiàn)的方法(Java 8 可以用
default
關(guān)鍵字在接口中定義默認(rèn)方法)。
區(qū)別:
- 接口主要用于對類的行為進(jìn)行約束,你實(shí)現(xiàn)了某個(gè)接口就具有了對應(yīng)的行為。抽象類主要用于代碼復(fù)用,強(qiáng)調(diào)的是所屬關(guān)系。
- 一個(gè)類只能繼承一個(gè)類,但是可以實(shí)現(xiàn)多個(gè)接口。
- 接口中的成員變量只能是
public static final
類型的,不能被修改且必須有初始值,而抽象類的成員變量默認(rèn) default,可在子類中被重新定義,也可被重新賦值
8.深拷貝和淺拷貝區(qū)別了解嗎?什么是引用拷貝?
淺拷貝:淺拷貝會在堆上創(chuàng)建一個(gè)新的對象(區(qū)別于引用拷貝的一點(diǎn)),不過,如果原對象內(nèi)部的屬性是引用類型的話,淺拷貝會直接復(fù)制內(nèi)部對象的引用地址,也就是說拷貝對象和原對象共用同一個(gè)內(nèi)部對象。
深拷貝:深拷貝會完全復(fù)制整個(gè)對象,包括這個(gè)對象所包含的內(nèi)部對象。
public class Address implements Cloneable{private String name;// 省略構(gòu)造函數(shù)、Getter&Setter方法@Overridepublic Address clone() {try {return (Address) super.clone();} catch (CloneNotSupportedException e) {throw new AssertionError();}}
}public class Person implements Cloneable {private Address address;// 省略構(gòu)造函數(shù)、Getter&Setter方法@Overridepublic Person clone() {//淺拷貝try {Person person = (Person) super.clone();return person;} catch (CloneNotSupportedException e) {throw new AssertionError();}}@Overridepublic Person clone() {try {//深拷貝Person person = (Person) super.clone();person.setAddress(person.getAddress().clone());return person;} catch (CloneNotSupportedException e) {throw new AssertionError();}
}
二、Object
1.Object 類的常見方法有哪些?
/*** native 方法,用于返回當(dāng)前運(yùn)行時(shí)對象的 Class 對象,使用了 final 關(guān)鍵字修飾,故不允許子類重寫。*/
public final native Class<?> getClass()
/*** native 方法,用于返回對象的哈希碼,主要使用在哈希表中,比如 JDK 中的HashMap。*/
public native int hashCode()
/*** 用于比較 2 個(gè)對象的內(nèi)存地址是否相等,String 類對該方法進(jìn)行了重寫以用于比較字符串的值是否相等。*/
public boolean equals(Object obj)
/*** native 方法,用于創(chuàng)建并返回當(dāng)前對象的一份拷貝。*/
protected native Object clone() throws CloneNotSupportedException
/*** 返回類的名字實(shí)例的哈希碼的 16 進(jìn)制的字符串。建議 Object 所有的子類都重寫這個(gè)方法。*/
public String toString()
/*** native 方法,并且不能重寫。喚醒一個(gè)在此對象監(jiān)視器上等待的線程(監(jiān)視器相當(dāng)于就是鎖的概念)。如果有多個(gè)線程在等待只會任意喚醒一個(gè)。*/
public final native void notify()
/*** native 方法,并且不能重寫。跟 notify 一樣,唯一的區(qū)別就是會喚醒在此對象監(jiān)視器上等待的所有線程,而不是一個(gè)線程。*/
public final native void notifyAll()
/*** native方法,并且不能重寫。暫停線程的執(zhí)行。注意:sleep 方法沒有釋放鎖,而 wait 方法釋放了鎖 ,timeout 是等待時(shí)間。*/
public final native void wait(long timeout) throws InterruptedException
/*** 多了 nanos 參數(shù),這個(gè)參數(shù)表示額外時(shí)間(以納秒為單位,范圍是 0-999999)。 所以超時(shí)的時(shí)間還需要加上 nanos 納秒。。*/
public final void wait(long timeout, int nanos) throws InterruptedException
/*** 跟之前的2個(gè)wait方法一樣,只不過該方法一直等待,沒有超時(shí)時(shí)間這個(gè)概念*/
public final void wait() throws InterruptedException
/*** 實(shí)例被垃圾回收器回收的時(shí)候觸發(fā)的操作*/
protected void finalize() throws Throwable { }
2.== 和 equals() 的區(qū)別
==
對于基本類型和引用類型的作用效果是不同的:
- 對于基本數(shù)據(jù)類型來說,
==
比較的是值。 - 對于引用數(shù)據(jù)類型來說,
==
比較的是對象的內(nèi)存地址。
equals()
不能用于判斷基本數(shù)據(jù)類型的變量,只能用來判斷兩個(gè)對象是否相等。equals()
方法存在于Object
類中,而Object
類是所有類的直接或間接父類,因此所有的類都有equals()
方法。
//object
public boolean equals(Object obj) {return (this == obj);
}
//String中重寫Object方法
public boolean equals(Object anObject) {if (this == anObject) {return true;}if (anObject instanceof String) {String anotherString = (String)anObject;int n = value.length;if (n == anotherString.value.length) {char v1[] = value;char v2[] = anotherString.value;int i = 0;while (n-- != 0) {if (v1[i] != v2[i])return false;i++;}return true;}}return false;
}
3. hashCode() 有什么用?
hashCode()
的作用是獲取哈希碼(int
整數(shù)),也稱為散列碼。這個(gè)哈希碼的作用是確定該對象在哈希表中的索引位置。
hashCode()
定義在 JDK 的 Object
類中,這就意味著 Java 中的任何類都包含有 hashCode()
函數(shù)。另外需要注意的是:Object
的 hashCode()
方法是本地方法,也就是用 C 語言或 C++ 實(shí)現(xiàn)的。
public native int hashCode();
4.為什么重寫 equals() 時(shí)必須重寫 hashCode() 方法?
equals
方法判斷兩個(gè)對象是相等的,那這兩個(gè)對象的hashCode
值也要相等。- 兩個(gè)對象有相同的
hashCode
值,他們也不一定是相等的(哈希碰撞)。
總結(jié)
如果兩個(gè)對象的hashCode
值相等,那這兩個(gè)對象不一定相等(哈希碰撞)。
如果兩個(gè)對象的hashCode
值相等并且equals()
方法也返回 true
,我們才認(rèn)為這兩個(gè)對象相等。
如果兩個(gè)對象的hashCode
值不相等,我們就可以直接認(rèn)為這兩個(gè)對象不相等。
三、String
1.String、StringBuffer、StringBuilder 的區(qū)別?
可變性
String
是不可變的(后面會詳細(xì)分析原因)。
StringBuilder
與 StringBuffer
都繼承自 AbstractStringBuilder
類,在 AbstractStringBuilder
中也是使用字符數(shù)組保存字符串,不過沒有使用 final
和 private
關(guān)鍵字修飾,最關(guān)鍵的是這個(gè) AbstractStringBuilder
類還提供了很多修改字符串的方法比如 append
方法。
abstract class AbstractStringBuilder implements Appendable, CharSequence {char[] value;public AbstractStringBuilder append(String str) {if (str == null)return appendNull();int len = str.length();ensureCapacityInternal(count + len);str.getChars(0, len, value, count);count += len;return this;}//...
}
線程安全性
StringBuffer
對方法加了同步鎖或者對調(diào)用的方法加了同步鎖,所以是線程安全的。
StringBuilder
并沒有對方法進(jìn)行加同步鎖,所以是非線程安全的。
性能
相同情況下使用 StringBuilder
相比使用 StringBuffer
僅能獲得 10%~15% 左右的性能提升,但卻要冒多線程不安全的風(fēng)險(xiǎn)。
對于三者使用的總結(jié):
- 操作少量的數(shù)據(jù): 適用
String
- 單線程操作字符串緩沖區(qū)下操作大量數(shù)據(jù): 適用
StringBuilder
- 多線程操作字符串緩沖區(qū)下操作大量數(shù)據(jù): 適用
StringBuffer
2.String 為什么是不可變的?
String
類中使用 final
關(guān)鍵字修飾字符數(shù)組來保存字符串
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {private final char value[];//...
}
//在 Java 9 之后,String、StringBuilder 與 StringBuffer 的實(shí)現(xiàn)改用 byte 數(shù)組存儲字符串。
public final class String implements java.io.Serializable,Comparable<String>, CharSequence {// @Stable 注解表示變量最多被修改一次,稱為“穩(wěn)定的”。@Stableprivate final byte[] value;
}abstract class AbstractStringBuilder implements Appendable, CharSequence {byte[] value;
}
String
真正不可變有下面幾點(diǎn)原因:
- 保存字符串的數(shù)組被
final
修飾且為私有的,并且String
類沒有提供/暴露修改這個(gè)字符串的方法。 String
類被final
修飾導(dǎo)致其不能被繼承,進(jìn)而避免了子類破壞String
不可變。
3.字符串拼接用“+” 還是 StringBuilder?
字符串對象通過“+”的字符串拼接方式,實(shí)際上是通過 StringBuilder
調(diào)用 append()
方法實(shí)現(xiàn)的,拼接完成之后調(diào)用 toString()
得到一個(gè) String
對象
4.String.equals() 和 Object.equals() 有何區(qū)別?
String
中的equals
方法是被重寫過的,比較的是 String 字符串的值是否相等。Object
的equals
方法是比較的對象的內(nèi)存地址
5.字符串常量池的作用了解嗎?
字符串常量池 是 JVM 為了提升性能和減少內(nèi)存消耗針對字符串(String 類)專門開辟的一塊區(qū)域,主要目的是為了避免字符串的重復(fù)創(chuàng)建。
6.String s1 = new String(“abc”);這句話創(chuàng)建了幾個(gè)字符串對象?
會創(chuàng)建 1 或 2 個(gè)字符串對象。
1、如果字符串常量池中不存在字符串對象“abc”的引用,那么它會在堆上創(chuàng)建兩個(gè)字符串對象,其中一個(gè)字符串對象的引用會被保存在字符串常量池中。
7.String#intern 方法有什么作用?
String.intern()
是一個(gè) native(本地)方法,其作用是將指定的字符串對象的引用保存在字符串常量池中,可以簡單分為兩種情況:
- 如果字符串常量池中保存了對應(yīng)的字符串對象的引用,就直接返回該引用。
- 如果字符串常量池中沒有保存了對應(yīng)的字符串對象的引用,那就在常量池中創(chuàng)建一個(gè)指向該字符串對象的引用并返回。
// 在堆中創(chuàng)建字符串對象”Java“
// 將字符串對象”Java“的引用保存在字符串常量池中
String s1 = "Java";
// 直接返回字符串常量池中字符串對象”Java“對應(yīng)的引用
String s2 = s1.intern();
// 會在堆中在單獨(dú)創(chuàng)建一個(gè)字符串對象
String s3 = new String("Java");
// 直接返回字符串常量池中字符串對象”Java“對應(yīng)的引用
String s4 = s3.intern();
// s1 和 s2 指向的是堆中的同一個(gè)對象
System.out.println(s1 == s2); // true
// s3 和 s4 指向的是堆中不同的對象
System.out.println(s3 == s4); // false
// s1 和 s4 指向的是堆中的同一個(gè)對象
System.out.println(s1 == s4); //true
8.String 類型的變量和常量做“+”運(yùn)算時(shí)發(fā)生了什么?
并不是所有的常量都會進(jìn)行折疊,只有編譯器在程序編譯期就可以確定值的常量才可以:
- 基本數(shù)據(jù)類型(
byte
、boolean
、short
、char
、int
、float
、long
、double
)以及字符串常量。 final
修飾的基本數(shù)據(jù)類型和字符串變量- 字符串通過 “+”拼接得到的字符串、基本數(shù)據(jù)類型之間算數(shù)運(yùn)算(加減乘除)、基本數(shù)據(jù)類型的位運(yùn)算(<<、>>、>>> )
引用的值在程序編譯期是無法確定的,編譯器無法對其進(jìn)行優(yōu)化。
String str1 = "str";
String str2 = "ing";
String str3 = "str" + "ing";
String str4 = str1 + str2;
String str5 = "string";
System.out.println(str3 == str4);//false
System.out.println(str3 == str5);//true
System.out.println(str4 == str5);//false//對象引用和“+”的字符串拼接方式,實(shí)際上是通過 StringBuilder 調(diào)用 append() 方法實(shí)現(xiàn)的,拼接完成之后調(diào)用 toString() 得到一個(gè) String 對象
String str4 = new StringBuilder().append(str1).append(str2).toString();//字符串使用 final 關(guān)鍵字聲明之后,可以讓編譯器當(dāng)做常量來處理。
final String str1 = "str";
final String str2 = "ing";
// 下面兩個(gè)表達(dá)式其實(shí)是等價(jià)的
String c = "str" + "ing";// 常量池中的對象
String d = str1 + str2; // 常量池中的對象
System.out.println(c == d);// true//編譯器在運(yùn)行時(shí)才能知道其確切值的話,就無法對其優(yōu)化。
final String str1 = "str";
final String str2 = getStr();
String c = "str" + "ing";// 常量池中的對象
String d = str1 + str2; // 在堆上創(chuàng)建的新的對象
System.out.println(c == d);// false
public static String getStr() {return "ing";
}``