知識點
類是用來描述物件的,而反射就可以理解為是用來描述類的。
類中的屬性包括:
- Class 類本身
- Package 類所在的包
- Field 類中的屬性
- Method 類中的方法
- Constructor 類中的構造方法
- Annotation 類中的註解
如何獲取Class
1.Class的靜態方法,forName("全類名")
2.類.class關鍵字
3.物件引用.getClass()方法 Object中的方法
Class中的常用方法
/*
0--預設不寫 1--public 2--private 4--protected 8--static 16--final 32--synchronized 64--volatile 128--transient 256--native 512--interface 1024--abstract
*/
int = getModifiers(); //獲取類的修飾符(許可權+特徵)
String = getName(); //獲取類的全類名
String = getSimpleName(); //獲取簡單名(只有類名 )
Class = getSuperClass(); //獲取當前父類的對應Class
Class[] = getInterfaces(); //獲取當前父類的介面
Package p = getPackage(); //獲取當前類所在的包
p.getName(); //獲取包的名字
Class[] = getClasses(); //獲取類中的內部類
Object = newInstance(); //獲取當前類的物件(相當於呼叫了類中的無引數的構造方法)如果類中不存在無引數的構造方法,就會丟擲NoSuchMethodException異常
Field = getField("屬性名"); //獲取類中的屬性(公有的 自己類+父類)
Field[] = getFields(); //獲取類中的全部屬性(公有的 自己類+父類)
Field = getDeclaredField("屬性名") //獲取當前類的屬性(公有 + 私有 自己類)
Field = getDeclaredFields() //獲取當前類的全部屬性(公有 + 私有 自己類)
如果想修改私有的屬性則需要設定屬性可以被操作
setAccessible()
public class TestMain {
public static void main(String[] args) {
try {
Class<?> clazz = Class.forName("com.lili.reflect.People");
Package aPackage = clazz.getPackage();
int modifiers = clazz.getModifiers();
System.out.println(modifiers);
Method[] methods = clazz.getMethods();
for (Method method : methods) {
System.out.println(method.getName());
}
System.out.println(aPackage);
Class<?>[] interfaces = clazz.getInterfaces();
for (Class c : interfaces) {
System.out.println(c.getName());
}
ArrayList<String> list = new ArrayList<>();
Class c = ArrayList.class;
Class superclass = c.getSuperclass();
while (superclass != null) {
System.out.println(superclass.getName());
superclass = superclass.getSuperclass();
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
利用反射修改String型別的值
/*
注意只能是繞過private去修改屬性的值,而不能去修改屬性的長度,因為是final修飾的。
String的不可變指的是長度+值的不可變
*/
public class ChangeString {
public static void main(String[] args) {
try {
String str = new String("abc");
System.out.println(str);
//1、利用反射技術獲取String的Class
Class clazz = str.getClass();
//2、獲取屬性
Field f = clazz.getDeclaredField("value");
//3、設定可以修改屬性的值
f.setAccessible(true);
//4、獲取屬性的值
char[] newChar = (char[])f.get(str);
//5、修改屬性的值
newChar[0] = 'xu';
newChar[1] = 'Li';
newChar[2] = 'Li';
System.out.println(str);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
利用反射呼叫類中的方法
可以獲取共有的方法,包括自己類的以及父類的
可以找到私有的方法,但是要通過setAccessible(true)
方法來執行私有的方法。
/**
* 測試使用反射獲取類中的方法
*/
public class TestMethod {
public static void main(String[] args) {
try {
//1、獲取People類對應的Class
Class clazz = People.class;
//2、獲取物件
People p = (People) clazz.newInstance();
//3、通過clazz獲取其中的方法,通過方法名以及方法的引數型別來定位方法。
Method m = clazz.getMethod("eat", String.class);
//4、呼叫方法,第一個引數是要執行方法的物件,第二個則是傳進去的引數列表
String n = (String) m.invoke(p, "lili要開始吃飯啦");
System.out.println(n);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
利用反射執行構造方法
/**
* 利用反射執行構造方法
*/
public class TestConstructor {
public static void main(String[] args) {
try {
//1、獲取People對應的Class
Class<People> clazz = People.class;
//2、獲取People中的構造方法,其中省去了構造方法的名稱,因為是與類同名
//無參的就是呼叫的無引數的構造方法
//有引數的就是傳的構造方法中形參的型別.class
Constructor<People> constructor = clazz.getConstructor(String.class);
//3、執行構造方法,同理引數就是要傳引數的實參
People people = constructor.newInstance("哈哈哈哈哈");
System.out.println(people);
} catch (Exception e) {
e.printStackTrace();
}
}
}
設計一個小工具
這個小工具可以代替我們自己建立物件的功能,通過傳遞一個字串,來幫我們建立一個物件,同時還能將物件內的所有屬性賦值。
其實這就是簡單的模擬了Spring中IOC思想的原理,IOC(Inversion Of Control)控制反轉:將物件的控制權反轉,交給Spring容器去處理;DI(Dependency Injection)依賴注入:Spring容器建立物件的同時幫我們自動的注入屬性的值。
public class MySpring {
//設計一個方法,將我們建立物件的過程交給該方法去執行。
//引數String型別的全類名
//返回值 建立出來的物件 Object型別--->再新增上DI依賴注入
public Object getBean(String classPath) {
Object obj = null;
//模擬輸入的實參
Scanner scanner = new Scanner(System.in);
System.out.println("請給"+ classPath +"的屬性賦值");
try {
//1、獲取該路徑下對應的Class
Class clazz = Class.forName(classPath);
//2、建立一個物件
obj = clazz.newInstance();
//使用set方法對物件的屬性進行賦值,找到每一個不同物件對應的set方法。
//也就是字串set+屬性的名字
//3、獲取類中的屬性
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
//獲取屬性的名稱
String fieldName = field.getName();
//改變屬性名中第一個字母的大小寫
String first = fieldName.substring(0, 1).toUpperCase();
//獲取屬性名中除開第一個字母的欄位
String last = fieldName.substring(1);
//拼接set方法
StringBuilder methodName = new StringBuilder("set");
methodName.append(first);
methodName.append(last);
//4、獲取屬性的型別
Class fieldType = field.getType();
//5、獲取方法
Method method = clazz.getMethod(methodName.toString(), fieldType);
//接收實參
System.out.println("請給"+ fieldName +"屬性賦值");
String value = scanner.nextLine();
/*為了解決引數型別不一致的問題,可以將引數的型別都設定未相應的包裝類,
並且將它們都轉換成String的型別,除了Char型別之外需要另外的判斷。
可以利用其它包裝類帶String型別的構造方法進行處理。
*/
Constructor con = fieldType.getConstructor(String.class);
//6、執行方法
method.invoke(obj, con.newInstance(value));
}
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}
}