Java反射全解析(使用、原理、問題、在Android中的應用)
阿新 • • 發佈:2021-01-19
單例模式
簡介
保證在記憶體中只有一個例項
餓漢式
public class Single01 { //類載入初始話 private static final Single01 INSTANCE = new Single01(); //私有構造器 private Single01(){ } //公開方法 public static Single01 getInstance(){ return INSTANCE; } public static void main(String[] args) { Single01 instance01 = Single01.getInstance(); Single01 instance02 = Single01.getInstance(); System.out.println(instance01 == instance02); } }
反射破解
import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public class Single01 { //類載入初始話 private static final Single01 INSTANCE = new Single01(); //私有構造器 private Single01(){ } //公開方法 public static Single01 getInstance(){ return INSTANCE; } public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { Single01 instance01 = Single01.getInstance(); //獲得class檔案 Class<Single01> single01Class = Single01.class; //獲取無參構造器 Constructor<Single01> constructor = single01Class.getDeclaredConstructor(); //暴力反射 constructor.setAccessible(true); //反射例項 Single01 single01 = constructor.newInstance(); System.out.println(single01 == instance01); } }
懶漢式
執行緒安全問題
/** * lazy load * 雖然按需求初始話,但是有執行緒安全的問題 * 這裡要加上volatile 防止指令重排 */ public class Single02 { private static Single02 INSTANCE; private Single02(){ } public static Single02 getINSTANCE(){ if (INSTANCE == null){ try { //測試 Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } INSTANCE = new Single02(); } return INSTANCE; } public static void main(String[] args) { for (int i = 0; i < 100; i++) { new Thread(()->{ System.out.println(Single02.getINSTANCE()); }).start(); } } }
解決 synchronized
/**
* lazy load
* 雖然按需求初始話,但是有執行緒安全的問題
* 加上 synchronized 效率會下降
*/
public class Single02 {
private static Single02 INSTANCE;
private Single02(){
}
public synchronized static Single02 getINSTANCE(){
if (INSTANCE == null){
try {
//測試
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
INSTANCE = new Single02();
}
return INSTANCE;
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(()->{
System.out.println(Single02.getINSTANCE());
}).start();
}
}
}
雙重檢測鎖
/**
* 雙重檢測鎖
*/
public class Single03 {
private static Single03 INSTANCE;
private Single03(){
}
public static Single03 getINSTANCE(){
if (INSTANCE == null){
synchronized (Single03.class){
if (INSTANCE == null){
try {
//測試
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
INSTANCE = new Single03();
}
}
}
return INSTANCE;
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(()->{
System.out.println(Single03.getINSTANCE());
}).start();
}
}
}
反射破解
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Single04 {
private static Single04 INSTANCE;
private Single04(){
}
public static Single04 getINSTANCE(){
if (INSTANCE == null){
synchronized (Single04.class){
if (INSTANCE == null){
try {
//測試
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
INSTANCE = new Single04();
}
}
}
return INSTANCE;
}
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Single04 instance01 = Single04.getINSTANCE();
Class<Single04> single04Class = Single04.class;
Constructor<Single04> constructor = single04Class.getDeclaredConstructor();
constructor.setAccessible(true);
Single04 single04 = constructor.newInstance();
System.out.println(single04 == instance01);
}
}
靜態內部類
//開始實現懶載入
public class Single05 {
private Single05(){
}
private static class HolderSingle05{
private static final Single05 INSTANCE = new Single05();
}
//公開方法
public static Single05 getInstance(){
return HolderSingle05.INSTANCE;
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(() -> {
System.out.println(Single05.getInstance());
}).start();
}
}
}
反射破解
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Single05 {
private Single05(){
}
//匿名內部類
private static class HolderSingle05{
private static final Single05 INSTANCE = new Single05();
}
//公開方法
public static Single05 getInstance(){
return HolderSingle05.INSTANCE;
}
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Single05 instance = Single05.getInstance();
Class<Single05> single05Class = Single05.class;
Constructor<Single05> declaredConstructor = single05Class.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
Single05 single05 = declaredConstructor.newInstance();
System.out.println(single05 == instance);
}
}
列舉 enum
//列舉類
public enum Single06 {
INSTANCE;
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
System.out.println(Single06.INSTANCE.hashCode());
}
}
}
反射破解
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
//列舉類
public enum Single06 {
INSTANCE;
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
System.out.println(Single06.INSTANCE.hashCode());
Class<Single06> single06Class = Single06.class;;
Constructor<Single06> declaredConstructor = single06Class.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
Single06 single06 = declaredConstructor.newInstance();
System.out.println(single06.hashCode());
}
}
丟擲異常 沒有空參構造
這裡可以看到如果是列舉的話會報Cannot reflectively create enum objects
通過反編譯可以看到構造器其實是有引數的
重新執行一下
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
//列舉類
public enum Single06 {
INSTANCE;
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
System.out.println(Single06.INSTANCE.hashCode());
Class<Single06> single06Class = Single06.class;;
//在構造器傳入指定引數
Constructor<Single06> declaredConstructor = single06Class.getDeclaredConstructor(String.class,int.class);
declaredConstructor.setAccessible(true);
Single06 single06 = declaredConstructor.newInstance();
System.out.println(single06.hashCode());
}
}
丟擲指定異常 It's ok