目錄
六.? Java反射機(jī)制以及動(dòng)態(tài)代理是基于什么原理
?6.1 反射機(jī)制:
6.2 反射例子:
6.3 動(dòng)態(tài)代理:
6.4 例子:
6.5 總結(jié):
6.5.1 代理模式
6.5.1.1動(dòng)態(tài)代理:
*6.5.1.2 JDK動(dòng)態(tài)代理:
*6.5.1.3 cglib動(dòng)態(tài)代理:
6.5.1.4 反射與動(dòng)態(tài)代理原理
6.5.2 應(yīng)用場(chǎng)景:
6.5.2.3 組成要素:
6.5.2.4 實(shí)現(xiàn)方式:
七. int 和 Integer 有什么區(qū)別?? Integer的值緩存范圍?
7.1? ?int:
7.2 Integer的值緩存:
7.3 考點(diǎn)分析:
7.3.1 理解自動(dòng)裝箱,拆箱
7.3.1.1 自動(dòng)裝箱實(shí)際上是一種語(yǔ)法糖。
7.3.1.2 語(yǔ)法糖解釋:
7.3.1.2.1 語(yǔ)法糖例子
7.3.1.2.1 .1自動(dòng)裝箱和拆箱:
7.3.1.2.1.2?泛型:
7.3.1.2.1.3?Lambda表達(dá)式:
7.3.2 對(duì)象的組成
7.3.2.1對(duì)象頭:
7.3.2.2對(duì)象實(shí)例:
7.3.2.3舉個(gè)繼承有父類的例子:
7.3.2.4舉個(gè)正常的例子:
7.3.2.5 tips:
7.3.2.6 小結(jié):
7.3.2.6.1 int和Integer有什么區(qū)別?
7.3.2.6.2 對(duì)象的內(nèi)存結(jié)構(gòu)是什么樣的?
六.? Java反射機(jī)制以及動(dòng)態(tài)代理是基于什么原理
?6.1 反射機(jī)制:
Java語(yǔ)言提供的一種基礎(chǔ)功能,賦予程序在運(yùn)行時(shí)自省(introspect)的能力。
通過反射我們可以直接操作類和對(duì)象:
比如獲取某個(gè)對(duì)象的類定義,獲取類聲明的屬性和方法,調(diào)用方法或者構(gòu)造對(duì)象,甚至可以運(yùn)行時(shí)修改類定義。
6.2 反射例子:
反射的示例,展示了如何獲取類的信息、創(chuàng)建對(duì)象和調(diào)用方法
通過 Class<?> clazz = MyClass.class
獲取了類 MyClass
的定義。然后使用反射獲取了類的名稱、屬性和方法,并輸出到控制臺(tái)。
接下來,我們使用反射創(chuàng)建了 MyClass
類的對(duì)象實(shí)例,并通過反射調(diào)用了 setName()
和 setAge()
方法來設(shè)置對(duì)象的屬性值。
最后,我們通過調(diào)用對(duì)象的 toString()
方法將對(duì)象信息輸出到控制臺(tái)。
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ReflectionExample {
public static void main(String[] args) throws Exception {
// 獲取類的定義
Class<?> clazz = MyClass.class;
// 獲取類的名稱
String className = clazz.getName();
System.out.println("Class Name: " + className);
// 獲取類聲明的屬性
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
String fieldName = field.getName();
System.out.println("Field Name: " + fieldName);
}
// 獲取類聲明的方法
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
String methodName = method.getName();
System.out.println("Method Name: " + methodName);
}
// 創(chuàng)建對(duì)象實(shí)例
Constructor<?> constructor = clazz.getDeclaredConstructor();
Object obj = constructor.newInstance();
// 調(diào)用方法
Method setNameMethod = clazz.getDeclaredMethod("setName", String.class);
setNameMethod.invoke(obj, "John Doe");
Method setAgeMethod = clazz.getDeclaredMethod("setAge", int.class);
setAgeMethod.invoke(obj, 30);
// 輸出對(duì)象信息
System.out.println(obj.toString());
}
}
class MyClass {
private String name;
private int age;
public MyClass() {
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Name: " + name + ", Age: " + age;
}
}
?輸出
Class Name: MyClass
Field Name: name
Field Name: age
Method Name: setName
Method Name: setAge
Name: John Doe, Age: 30
展示了如何使用反射獲取類的信息、創(chuàng)建對(duì)象實(shí)例和調(diào)用方法。通過反射,我們可以在運(yùn)行時(shí)動(dòng)態(tài)地操作類和對(duì)象,靈活應(yīng)對(duì)不同的需求
6.3 動(dòng)態(tài)代理:
是一種方便運(yùn)行時(shí)動(dòng)態(tài)構(gòu)建代理,動(dòng)態(tài)處理代理方法調(diào)用的機(jī)制,很多場(chǎng)景都是利用類似機(jī)制做到的。
比如用來面向切面的編程(AOP)
6.4 例子:
創(chuàng)建一個(gè)實(shí)現(xiàn)InvocationHandler
接口的類,用于處理代理方法的調(diào)用
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class LogInvocationHandler implements InvocationHandler {
private Object target; // 目標(biāo)對(duì)象
public LogInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 在方法執(zhí)行前輸出日志
System.out.println("Before invoking " + method.getName() + "()");
// 調(diào)用目標(biāo)對(duì)象的方法
Object result = method.invoke(target, args);
// 在方法執(zhí)行后輸出日志
System.out.println("After invoking " + method.getName() + "()");
return result;
}
}
使用Proxy
類來動(dòng)態(tài)生成代理對(duì)象,并將目標(biāo)對(duì)象和LogInvocationHandler
關(guān)聯(lián)起來
import java.lang.reflect.Proxy;
public class Main {
public static void main(String[] args) {
UserService userService = new UserServiceImpl(); // 原始的目標(biāo)對(duì)象
// 創(chuàng)建LogInvocationHandler實(shí)例
LogInvocationHandler handler = new LogInvocationHandler(userService);
// 使用Proxy類動(dòng)態(tài)生成代理對(duì)象
UserService proxy = (UserService) Proxy.newProxyInstance(
userService.getClass().getClassLoader(),
userService.getClass().getInterfaces(),
handler
);
// 調(diào)用代理對(duì)象的方法
proxy.addUser("Alice");
proxy.deleteUser("Bob");
}
}
使用了Proxy
類的newProxyInstance()
方法來動(dòng)態(tài)生成代理對(duì)象。
newProxyInstance()
方法接受三個(gè)參數(shù):
-
ClassLoader loader
: 指定用于定義代理類的類加載器。我們可以使用目標(biāo)對(duì)象的類加載器,通過userService.getClass().getClassLoader()
獲取。 -
Class<?>[] interfaces
: 指定代理類要實(shí)現(xiàn)的接口列表。我們可以通過userService.getClass().getInterfaces()
獲取目標(biāo)對(duì)象實(shí)現(xiàn)的接口數(shù)組。 -
InvocationHandler handler
: 指定用于處理代理方法調(diào)用的InvocationHandler
實(shí)例。在我們的例子中,我們使用了自定義的LogInvocationHandler
作為處理器。
每次調(diào)用代理對(duì)象的方法時(shí),都會(huì)經(jīng)過LogInvocationHandler
的invoke()
方法。在方法執(zhí)行前,會(huì)輸出相應(yīng)的日志信息(例如"Before invoking addUser()"),然后調(diào)用目標(biāo)對(duì)象的對(duì)應(yīng)方法,最后輸出方法執(zhí)行后的日志信息(例如"After invoking addUser()")。
6.5 總結(jié):
6.5.1 代理模式
通過代理靜默地解決一些業(yè)務(wù)無關(guān)的問題,比如遠(yuǎn)程、安全、事務(wù)、日志、資源關(guān)
閉......讓應(yīng)用開發(fā)者可以只關(guān)心他的業(yè)務(wù)
靜態(tài)代理:
事先寫好代理類,可以手工編寫,也可以用工具生成。缺點(diǎn)是每個(gè)業(yè)務(wù)類都要
對(duì)應(yīng)一個(gè)代理類,非常不靈活。
6.5.1.1動(dòng)態(tài)代理:
運(yùn)行時(shí)自動(dòng)生成代理對(duì)象。缺點(diǎn)是生成代理代理對(duì)象和調(diào)用代理方法都要額外
花費(fèi)時(shí)間。
*6.5.1.2 JDK動(dòng)態(tài)代理:
基于Java反射機(jī)制實(shí)現(xiàn),必須要實(shí)現(xiàn)了接口的業(yè)務(wù)類才能用這種辦法生
成代理對(duì)象。新版本也開始結(jié)合ASM機(jī)制。
*6.5.1.3 cglib動(dòng)態(tài)代理:
基于ASM機(jī)制實(shí)現(xiàn),通過生成業(yè)務(wù)類的子類作為代理類。
Java 反射機(jī)制的常見應(yīng)用:動(dòng)態(tài)代理(AOP、RPC)、提供第三方開發(fā)者擴(kuò)展能力(Servlet容
器,JDBC連接)、第三方組件創(chuàng)建對(duì)象(DI)
6.5.1.4 反射與動(dòng)態(tài)代理原理
1 關(guān)于反射
反射最大的作用之一就在于我們可以不在編譯時(shí)知道某個(gè)對(duì)象的類型,而在運(yùn)行時(shí)通過提供
完整的”包名+類名.class”得到。注意:不是在編譯時(shí),而是在運(yùn)行時(shí)。
功能:
?在運(yùn)行時(shí)能判斷任意一個(gè)對(duì)象所屬的類。
?在運(yùn)行時(shí)能構(gòu)造任意一個(gè)類的對(duì)象。
?在運(yùn)行時(shí)判斷任意一個(gè)類所具有的成員變量和方法。
?在運(yùn)行時(shí)調(diào)用任意一個(gè)對(duì)象的方法。
說大白話就是,利用Java反射機(jī)制我們可以加載一個(gè)運(yùn)行時(shí)才得知名稱的class,獲悉其構(gòu)造
方法,并生成其對(duì)象實(shí)體,能對(duì)其fields設(shè)值并喚起其methods。
6.5.2 應(yīng)用場(chǎng)景:
反射技術(shù)常用在各類通用框架開發(fā)中。因?yàn)闉榱吮WC框架的通用性,需要根據(jù)配置文件加載
不同的對(duì)象或類,并調(diào)用不同的方法,這個(gè)時(shí)候就會(huì)用到反射——運(yùn)行時(shí)動(dòng)態(tài)加載需要加載
的對(duì)象。
6.5.2.1特點(diǎn):
由于反射會(huì)額外消耗一定的系統(tǒng)資源,因此如果不需要?jiǎng)討B(tài)地創(chuàng)建一個(gè)對(duì)象,那么就不需要
用反射。另外,反射調(diào)用方法時(shí)可以忽略權(quán)限檢查,因此可能會(huì)破壞封裝性而導(dǎo)致安全問
題。
6.5.2.2 動(dòng)態(tài)代理
為其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪問。在某些情況下,一個(gè)對(duì)象不適合或者不
能直接引用另一個(gè)對(duì)象,而代理對(duì)象可以在兩者之間起到中介的作用(可類比房屋中介,房
東委托中介銷售房屋、簽訂合同等)。
所謂動(dòng)態(tài)代理,就是實(shí)現(xiàn)階段不用關(guān)心代理誰(shuí),而是在運(yùn)行階段才指定代理哪個(gè)一個(gè)對(duì)象
(不確定性)。如果是自己寫代理類的方式就是靜態(tài)代理(確定性)。
6.5.2.3 組成要素:
(動(dòng)態(tài))代理模式主要涉及三個(gè)要素:
其一:抽象類接口
其二:被代理類(具體實(shí)現(xiàn)抽象接口的類)
其三:動(dòng)態(tài)代理類:實(shí)際調(diào)用被代理類的方法和屬性的類
6.5.2.4 實(shí)現(xiàn)方式:
實(shí)現(xiàn)動(dòng)態(tài)代理的方式很多,比如 JDK 自身提供的動(dòng)態(tài)代理,就是主要利用了反射機(jī)制。還有
其他的實(shí)現(xiàn)方式,比如利用字節(jié)碼操作機(jī)制,類似 ASM、CGLIB(基于 ASM)、Javassist
等。
舉例,常可采用的JDK提供的動(dòng)態(tài)代理接口InvocationHandler來實(shí)現(xiàn)動(dòng)態(tài)代理類。其中invoke
方法是該接口定義必須實(shí)現(xiàn)的,它完成對(duì)真實(shí)方法的調(diào)用。通過InvocationHandler接口,所
有方法都由該Handler來進(jìn)行處理,即所有被代理的方法都由InvocationHandler接管實(shí)際的處
理任務(wù)。此外,我們??梢栽趇nvoke方法實(shí)現(xiàn)中增加自定義的邏輯實(shí)現(xiàn),實(shí)現(xiàn)對(duì)被代理類的
業(yè)務(wù)邏輯無侵入。
七. int 和 Integer 有什么區(qū)別?? Integer的值緩存范圍?
?Java雖然被稱為面向?qū)ο蟮恼Z(yǔ)言,但是原始數(shù)據(jù)類型仍然是重要的組成元素,經(jīng)常考察原始數(shù)據(jù)類型和包裝類等Java語(yǔ)言特性。
7.1? ?int:
int是我們是整型數(shù)字,是Java的8個(gè)原始數(shù)據(jù)類型(boolean,byte,short,char,int ,float,double,long)之一。
Java語(yǔ)言雖然號(hào)稱一切都是對(duì)象,但原始數(shù)據(jù)類型是例外。
Integer 是 int 對(duì)應(yīng)的包裝類,有一個(gè)int 類型的字段存儲(chǔ)數(shù)據(jù),并且提供了基本操作,如數(shù)學(xué)運(yùn)算,int和字符串之間的轉(zhuǎn)換等
Java 5 中,引入了自動(dòng)裝箱和自動(dòng)拆箱的功能(boxing/unboxing),Java根據(jù)上下文,自動(dòng)進(jìn)行轉(zhuǎn)換,簡(jiǎn)化了相關(guān)編程。
7.2 Integer的值緩存:
涉及到Java5 中另一個(gè)改進(jìn),構(gòu)建Integer對(duì)象的傳統(tǒng)方式是直接調(diào)用構(gòu)造器,直接進(jìn)行new一個(gè)對(duì)象。
根據(jù)實(shí)踐我們發(fā)現(xiàn)大部分?jǐn)?shù)據(jù)都是集中在有限的,較小的數(shù)值范圍,因此,在Java5中新增加靜態(tài)工廠方法valueOf,調(diào)用時(shí)會(huì)利用緩存機(jī)制,帶來了性能改進(jìn),默認(rèn)值是-128到127之間。
7.3 考點(diǎn)分析:
上面的回答覆蓋了,Java里面的兩個(gè)基本要素,原始數(shù)據(jù)類型,包裝類。
自然的就擴(kuò)展:自動(dòng)裝箱,自動(dòng)拆箱機(jī)制,可能會(huì)考察對(duì)封裝類的一些設(shè)計(jì)和實(shí)踐。
7.3.1 理解自動(dòng)裝箱,拆箱
7.3.1.1 自動(dòng)裝箱實(shí)際上是一種語(yǔ)法糖。
javac替我們自動(dòng)把裝箱轉(zhuǎn)換為Integer.valueOf(),把拆箱替換為Integer.intValue(),調(diào)用的是Integer.valueOf,采取緩存。
valueOf()
方法中,它是Integer
類的靜態(tài)方法,用于將基本類型int
或字符串轉(zhuǎn)換為對(duì)應(yīng)的Integer
對(duì)象。在valueOf()
方法中,針對(duì)-128到127范圍內(nèi)的整數(shù)值會(huì)使用緩存,返回預(yù)先創(chuàng)建的對(duì)象,而不是每次都創(chuàng)建新的對(duì)象。
7.3.1.2 語(yǔ)法糖解釋:
可以理解為Java平臺(tái)為我們自動(dòng)進(jìn)行的一些轉(zhuǎn)換,保證不同的寫法在運(yùn)行時(shí)等價(jià),發(fā)生在編譯階段,生成的字節(jié)碼是一樣的。
7.3.1.2.1 語(yǔ)法糖例子
7.3.1.2.1 .1自動(dòng)裝箱和拆箱:
通過自動(dòng)裝箱和拆箱的語(yǔ)法糖,我們可以直接在基本類型和對(duì)應(yīng)的包裝類之間進(jìn)行轉(zhuǎn)換,無需手動(dòng)編寫繁瑣的轉(zhuǎn)換代碼。
int num = 10;
Integer integer = num; // 自動(dòng)裝箱
int result = integer; // 自動(dòng)拆箱
7.3.1.2.1.2?泛型:
Java的泛型提供了類型安全的編程方式,使代碼更加靈活和易讀。在使用泛型時(shí),編譯器會(huì)進(jìn)行類型擦除,將泛型參數(shù)替換為實(shí)際的類型。例如,我們可以聲明一個(gè)List來存儲(chǔ)指定類型的元素,在編譯時(shí)會(huì)進(jìn)行類型檢查和轉(zhuǎn)換。
泛型解釋:
定義了一個(gè)泛型類GenericClass<T>
,它有一個(gè)類型參數(shù)T
。這個(gè)類包含了一個(gè)成員變量value
和對(duì)應(yīng)的getter和setter方法
public class GenericClass<T> {
private T value;
public void setValue(T value) {
this.value = value;
}
public T getValue() {
return value;
}
}
?在使用該泛型類時(shí),我們可以傳入不同的類型參數(shù)
GenericClass<String> strObj = new GenericClass<>();
strObj.setValue("Hello");
String strValue = strObj.getValue();
GenericClass<Integer> intObj = new GenericClass<>();
intObj.setValue(10);
Integer intValue = intObj.getValue();
我們分別實(shí)例化了兩個(gè)GenericClass
對(duì)象:strObj
和intObj
。一個(gè)使用了String
類型作為類型參數(shù),另一個(gè)使用了Integer
類型作為類型參數(shù)。
在編譯時(shí),由于類型擦除的原因,編譯器會(huì)將T
替換為實(shí)際的類型或者限定類型。在上述代碼中,泛型參數(shù)T
會(huì)被擦除為Object
類型。因此,編譯器會(huì)將代碼轉(zhuǎn)換成如下形式
public class GenericClass {
private Object value;
public void setValue(Object value) {
this.value = value;
}
public Object getValue() {
return value;
}
}
7.3.1.2.1.3?Lambda表達(dá)式:
Lambda表達(dá)式是一種語(yǔ)法糖,它可以簡(jiǎn)化匿名函數(shù)的編寫。通過Lambda表達(dá)式,我們可以以更精煉的方式表示函數(shù)式接口的實(shí)現(xiàn)。
匿名函數(shù):
一種沒有具體名稱的函數(shù)。它可以用來表示一段可執(zhí)行的代碼塊或邏輯,但沒有像普通函數(shù)那樣被命名。匿名函數(shù)通常用于函數(shù)式編程中,以便在需要時(shí)傳遞給其他函數(shù)或方法作為參數(shù)。
?例子1:
List<Integer> numbers = Arrays.asList(5, 3, 1, 4, 2);
// 使用匿名函數(shù)進(jìn)行升序排序
Collections.sort(numbers, new Comparator<Integer>() {
@Override
public int compare(Integer num1, Integer num2) {
return num1.compareTo(num2);
}
});
// 使用Lambda表達(dá)式進(jìn)行升序排序
Collections.sort(numbers, (num1, num2) -> num1.compareTo(num2));
System.out.println(numbers); // 輸出:[1, 2, 3, 4, 5]
例子2:
int[] numbers = {1, 2, 3, 4, 5};
for (int num : numbers) {
System.out.println(num);
}
//對(duì)比Lambda
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
numbers.forEach(num -> System.out.println(num));
7.3.2 對(duì)象的組成
?對(duì)象由三部分組成,對(duì)象頭,對(duì)象實(shí)例,對(duì)齊填充。
7.3.2.1對(duì)象頭:
一般是十六個(gè)字節(jié),包括兩部分,
第一部分有哈希碼,鎖狀態(tài)標(biāo)志,線程持有的 鎖,偏向線程id,gc分代年齡等。
第二部分是類型指針,也就是對(duì)象指向它的類元數(shù)據(jù)指針, 可以理解,對(duì)象指向它的類。
7.3.2.2對(duì)象實(shí)例:
就是對(duì)象存儲(chǔ)的真正有效信息,也是程序中定義各種類型的字段包括父類繼承的和子 類定義的。
虛擬機(jī)對(duì)對(duì)象字段的存儲(chǔ)順序不涉及重排序的概念。重排序主要指的是編譯器或處理器對(duì)指令執(zhí)行順序進(jìn)行優(yōu)化的行為,而不是虛擬機(jī)對(duì)對(duì)象字段在內(nèi)存中的排列順序進(jìn)行優(yōu)化
第三部分對(duì)齊填充只是一個(gè)類似占位符的作用,因?yàn)閮?nèi)存的使用都會(huì)被填充為八字節(jié)的倍數(shù)。
7.3.2.3舉個(gè)繼承有父類的例子:
定義了一個(gè)Person
類和一個(gè)繼承自Person
的Employee
類。Person
類有一個(gè)私有字段name
和一個(gè)公共方法getName()
用于獲取姓名。Employee
類除了繼承了Person
類的字段和方法外,還添加了一個(gè)私有字段employeeId
和一個(gè)公共方法getEmployeeId()
用于獲取員工ID。
public class Person {
private String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public class Employee extends Person {
private int employeeId;
public Employee(String name, int employeeId) {
super(name);
this.employeeId = employeeId;
}
public int getEmployeeId() {
return employeeId;
}
}
創(chuàng)建一個(gè)Employee
對(duì)象時(shí),它在內(nèi)存中的布局如下
對(duì)象頭(Header):16字節(jié)
├── 哈希碼、鎖狀態(tài)標(biāo)志、線程持有的鎖、偏向線程ID、GC分代年齡等信息
└── 類型指針,指向Employee類的元數(shù)據(jù)
對(duì)象實(shí)例(Instance Data):
├── name字段(String類型,引用) - 繼承自Person類
└── employeeId字段(int類型) - Employee類特有的字段
對(duì)齊填充(Padding):占位符,使對(duì)象的總大小為8字節(jié)的倍數(shù)
Employee
對(duì)象的實(shí)例數(shù)據(jù)部分包含了兩個(gè)字段。其中,name
字段是從Person
類繼承而來的,它是一個(gè)引用類型的字段,用于存儲(chǔ)姓名信息。而employeeId
字段是Employee
類特有的字段,它是一個(gè)整數(shù)類型的字段,用于存儲(chǔ)員工ID。
Employee
對(duì)象的實(shí)例數(shù)據(jù)部分存儲(chǔ)了name
字段(繼承自Person
類)和employeeId
字段(Employee
類特有),這些字段組成了對(duì)象的真正有效信息
7.3.2.4舉個(gè)正常的例子:
定義了一個(gè)Person
類,它有兩個(gè)字段:name
和age
。
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
創(chuàng)建Person
對(duì)象時(shí),它在內(nèi)存中的布局大致如下所示:
對(duì)象頭(Header):16字節(jié)
├── 哈希碼、鎖狀態(tài)標(biāo)志、線程持有的鎖、偏向線程ID、GC分代年齡等信息
└── 類型指針,指向Person類的元數(shù)據(jù)
對(duì)象實(shí)例(Instance Data):
├── name字段(String類型,引用)
└── age字段(int類型)
對(duì)齊填充(Padding):占位符,使對(duì)象的總大小為8字節(jié)的倍數(shù)
對(duì)象頭占據(jù)了16字節(jié),并包含了對(duì)象的哈希碼、鎖狀態(tài)等信息,以及指向Person
類元數(shù)據(jù)的類型指針。接下來,對(duì)象實(shí)例部分包含了name
和age
兩個(gè)字段,分別是一個(gè)引用類型的String
和一個(gè)基本類型的int
。在內(nèi)存中,這兩個(gè)字段按照定義的順序存儲(chǔ)。
需要注意的是,虛擬機(jī)對(duì)對(duì)象字段的存儲(chǔ)順序不涉及重排序的概念。重排序主要指的是編譯器或處理器對(duì)指令執(zhí)行順序進(jìn)行優(yōu)化的行為,而不是虛擬機(jī)對(duì)對(duì)象字段在內(nèi)存中的排列順序進(jìn)行優(yōu)化。因此,在這個(gè)例子中,虛擬機(jī)不會(huì)影響對(duì)象實(shí)例中字段的存儲(chǔ)順序。
7.3.2.5 tips:
除了存儲(chǔ)空間的區(qū)別外,基本數(shù)據(jù)類型是有默認(rèn)值的,而對(duì)象數(shù)據(jù)類型沒有默認(rèn)值。比如
從數(shù)據(jù)庫(kù)中查詢用戶年齡,如果用戶并沒有設(shè)置年齡信息,數(shù)據(jù)庫(kù)中代表年齡的列age =null
,那么在使用基本數(shù)據(jù)類型接收年齡值的時(shí)候就無法區(qū)分用戶是年齡為0還是未設(shè)置年齡的情
況,所以決定使用int還是Integer的時(shí)候除了考慮性能因素還要考慮業(yè)務(wù)場(chǎng)景。
7.3.2.6 小結(jié):
7.3.2.6.1 int和Integer有什么區(qū)別?
1. int:是Java的8個(gè)原始數(shù)據(jù)類型之一(boolean,byte,char,short,int,long,floa
t,double)
2. Integer:是int對(duì)應(yīng)的包裝類,是引用類型。在Java5中,引入了自動(dòng)裝箱和自動(dòng)拆箱
功能,Java可以根據(jù)上下文,自動(dòng)進(jìn)行轉(zhuǎn)化,極大的簡(jiǎn)化了相關(guān)編程。自動(dòng)裝箱/自動(dòng)拆箱發(fā)
生在編譯期,自己調(diào)用valueOf和intValue方法來使用緩存機(jī)制(默認(rèn)緩存是-128到127之
間)。注意:new 不使用緩存
3. int訪問是直接訪問數(shù)據(jù)內(nèi)存地址,Integer是通過引用找到數(shù)據(jù)內(nèi)存地址
4. 內(nèi)存空間占用:Integer大于int
5. 數(shù)據(jù)操作效率上:int大于Integer
6. 線程安全方面:int等原始數(shù)據(jù)類型需要使用并發(fā)相關(guān)手段。Integer等包裝類可以使
用類似AtomicInteger、AtomicLong等這樣的線程安全類。
7. int等原始數(shù)據(jù)類型和Java泛型不能配合使用
8. Integer和String一樣有final修飾,是不可變類型
9. Integer中定義了bytes常量,避免了因?yàn)榄h(huán)境 64位或32位不同造成的影響
實(shí)踐中,建議避免無意中的裝箱、拆箱行為文章來源:http://www.zghlxwxcb.cn/news/detail-661771.html
7.3.2.6.2 對(duì)象的內(nèi)存結(jié)構(gòu)是什么樣的?
在HotSpot虛擬機(jī)中,對(duì)象在內(nèi)存中存儲(chǔ)的布局都可以分為3塊區(qū)域。
1. 對(duì)象頭(Header)
包含兩部分信息:
1. Mark Word:用于存儲(chǔ)對(duì)象自身的運(yùn)行時(shí)數(shù)據(jù),如:哈希碼(HashCode),GC
分代年齡,鎖狀態(tài)標(biāo)志,線程持有的鎖,偏向線程ID,偏向時(shí)間戳等,
2. 類型指針:即對(duì)象指向它的類元數(shù)據(jù)的指針,虛擬機(jī)通過這個(gè)指針來確定這個(gè)
對(duì)象是哪個(gè)類的實(shí)例。
另外,如果對(duì)象是Java數(shù)組,那在對(duì)象頭中還必須有一塊用國(guó)語(yǔ)記錄數(shù)組長(zhǎng)度的數(shù)
據(jù)。
2. 實(shí)例數(shù)據(jù)(Instance Data):對(duì)象真正存儲(chǔ)的有效信息,也是在程序代碼中所定義的
各種類型的字段內(nèi)容。
3. 對(duì)齊填充(Padding):對(duì)齊填充并不是必然存在的,也沒有特別的含義,它僅僅起
著占位符的作用。使對(duì)象的大小必須是8字節(jié)的整數(shù)倍。文章來源地址http://www.zghlxwxcb.cn/news/detail-661771.html
到了這里,關(guān)于2023年Java核心技術(shù)面試第三篇(篇篇萬(wàn)字精講)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!