Java涉及知識點
- 裝箱和拆箱
- 反射及設置對象訪問權限原理
- 函數參數的值傳遞和引用傳遞
- Integer原理及緩存機制
- 實現一個函數來交換參數的兩個值
1. 裝包和拆包
java提供的基本類型以及其對於的包裝類型
基本類型 | 對於包裝類型 |
---|---|
byte | Byte |
char | Character |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
void | Void |
以Integer爲例子介紹裝箱和拆箱:”
裝箱存在的情況:
int 賦值給Integer 即基本類型賦值給包裝類型時
Integer i = 1; 等價於 Integer i = Integer.valueOf(1); java自動完成裝箱操作
拆箱存在的情況:
int與Integer比較 及基本類型和包裝類型比較時
Integer i = new Integer(1);
int j = 1;
i == j //等價於 i.intValue() == j i對象會自動拆箱int類型和j進行比較
2. 反射及設置對象訪問權限原理
JAVA反射機制是運行過程中,任意實體都可可以知道這個實體類的對象的所有方法和屬性信息
對於任何對象都可以調用它的任意方法和屬性,這種動態獲取信息以及動態調用對象方法的功能稱爲java語言的反射機制。
這裏我們反射的目標是修改某個類的private屬性值。以Integer爲例:
//獲取Integer類對象的方式
//Class clazz = Class.forName("java.lang.Integer");
Class clazz = Integer.class;
//獲取Integer類中私有屬性value的域 getDeclaredField獲取類本身對應的所有訪問
//權限的屬性 getField獲取類以及父類所有的public屬性
Field field = clazz.getDeclaredField("value");
field.setAccessible(true); //設置private域訪問權限
Integer a = 1;
field.set(a, 2);//將a 對象中的value屬性設置爲2
對象訪問權限原理
反着訪問對象的原理主要看Field類的setAccessible和set即可。一個設置域的可見性,一個設置屬性值。
File類聲明:public final
class Field extends AccessibleObject implements Member
//setAccessible源碼 父類AccessibleObject的方法
public void setAccessible(boolean flag) throws SecurityException {
SecurityManager sm = System.getSecurityManager();
if (sm != null) sm.checkPermission(ACCESS_PERMISSION);
setAccessible0(this, flag);//前面是一安全管理器判斷,這裏纔是真正設置的地方
}
private static void setAccessible0(AccessibleObject obj, boolean flag)
throws SecurityException
{
if (obj instanceof Constructor && flag == true) {
Constructor<?> c = (Constructor<?>)obj;
if (c.getDeclaringClass() == Class.class) {
throw new SecurityException("Cannot make a java.lang.Class" +
" constructor accessible");
}
}
obj.override = flag; //field父類中的override屬性設置爲flag,也就是我們設置的true
}
//set Feild中的set方法
public void set(Object obj, Object value)
throws IllegalArgumentException, IllegalAccessException
{
if (!override) {//同樣是判斷field父類中的override,爲true則不檢查對象訪問權限 直接設值
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, obj, modifiers);
}
}
getFieldAccessor(obj).set(obj, value);
}
3. 函數參數的值傳遞和引用傳遞
java中的函數參數傳遞分爲值傳遞和引用傳遞。實際上都可以認爲是值傳遞(引用傳遞實質傳遞的是引用的值(可以看作C++中指針,存放地址的變量),也同樣需要拷貝副本,不過不是對象的副本,而且存放對象地址的變量的副本)。
值傳遞:針對於基本數據類型
引用傳遞:針對於對象
int i = 1
void add1 (int i){//值傳遞 在i參數傳入的時候 實際上會拷貝i的一副本,然後在函數中進行操作
i++;//實際操作的是i的副本,所以函數外部的i變量不受影響
}
System.out.println(i);//輸出爲1
Integer j = 1;// j = Integer.valueOf(1)
void add2 (Integer j){//引用傳遞 在j參數傳入的時候 實際上會拷貝j對象的引用 然後傳入函數中進行操作
j++; //裝箱和拆箱的等價? j=j+1 j = Integer.valueOf(j.intValue()+1) 實際上是副本j引用指向了新的Integer對象,外面的j引用還是指向原來的對象
}
System.out.println(j);//輸出同樣爲1
4. Integer原理及緩存機制
Integer是int基本數據類型的包裝類,無非是在int基本類型的基礎上增加了一些操作和其他屬性。
Integer的實際對應int值是通過intValue()方法獲取的,源碼如下:
private final int value;//對應int基本類型的數值 是一個常量整型
public int intValue() {
return value;
}
前面說過的裝箱用到的一個方法是valueOf(),讓我們看看源碼:
//可以看到傳入的i 先和IntegerCache比較 在IntegerCache中則返回IntegerCache中的Integer不存在則new一個Integer對象
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
IntegerCache實現如下:
//IntegerCache是個Integer的內部類,在類加載的時候創建了256個緩存Integer對象,範圍-128至127
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int 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_VALUE
h = 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() {}
}
知道Integer緩存的存在,下面我們看看下面的幾個例子:
注:== 對象比較比較的是對象的引用是否相等 equals則根據對象內部的實現情況進行比較
Integer i = 1; //i = Integer.valueOf(1) 取緩存對象 IntegerCache.cache[129]
Integer j = 1; //j = Integer.valueOf(1) 取緩存對象 IntegerCache.cache[129]
System.out.println(i == j);//輸出true i 和j指向同一個對象
Integer i = 1; //i = Integer.valueOf(1) 取緩存對象 IntegerCache.cache[129]
Integer j = new Integer(1); // 新創建一個對象
System.out.println(i == j);//輸出false i 和j指向的不是同一個對象
Integer i = 128; //i = Integer.valueOf(128) 不在緩存訪問內 new Integer(128)
Integer j = 128; //j = Integer.valueOf(128) 不在緩存訪問內 new Integer(128)
System.out.println(i == j); //輸出false i 和j指向的不是同一個對象
所以Integer對象在比較是否相等的時候 不要用 == 用equals Integer內部實現了自己用equals,源碼如下:
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
5. 實現一個函數來交換參數的兩個值
前面已經補充了一些Java的相關知識點了,現在我們就來實現一個函數來交換參數的兩個值
public class Main {
static void swap1(Integer a, Integer b){ //第3節已經說明了 這裏函數中只是操作的引用副本,是不影響函數外a,b的變化的
Integer temp = a;
a = b;
b = temp;
}//輸出a=1 b=2
static void swap2(Integer a, Integer b) throws NoSuchFieldException, IllegalAccessException {//通過反射機制來修改引用指向對象的value值即改改變函數外a,b中的value數屬性
Field field = Integer.class.getDeclaredField("value");//value是private final
field.setAccessible(true);//繞過安全檢查
//temp = 1 temp ->Integer.valueOf(a.intValue()) -> Integer.valueOf(1) ->IntegerCache.cache[129]
Integer temp = a.intValue();
//public void set(Object obj, Object value)
//a.value->Integer.valueOf(b.intValue()).intValue(); 修改a指向對象中value值爲2
//a指向的是緩存中的IntegerCache.cache[129] 所以IntegerCache.cache[129]
//中的value被修改成2
field.set(a, b.intValue());
//b -> IntegerCache.cache[130]
//b.value->IntegerCache.cache[129].value 所以 IntegerCache.cache[130] IntegerCache.cache[129] value都是2 所以a b中value 都是2
field.set(b, temp);
}//輸出a=2 b=2
static void swap3(Integer a, Integer b) throws NoSuchFieldException, IllegalAccessException {
Field field = Integer.class.getDeclaredField("value");
field.setAccessible(true);//繞過安全檢查
Integer temp = new Integer(a.intValue());//和swap2唯一區區別就在這一行
field.set(a, b.intValue()); //a->Integer.valueOf(b.intValue()).intValue();
field.set(b,temp);//b.value->new Integer(a.intValue()).intValue() 而不是修改成IntegerCache.cache[129].intValue();
}//輸出a=2 b=1 但是存在一個問題就是同樣修改了IntegerCache中緩存值 後續存在隱患
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
Integer a = 1, b = 2; //裝箱操作 等價於 Integer a = Integer.valueOf(1); Integer b = Integer.valueOf(2);
swap1(a, b);
System.out.println("a="+a+" b="+b);//輸出a=1 b=2
}
}