類是對象,類是java.lang.Class類的實例對象java
public class MyClass {
public static void main(String[] args) {
// People的實例對象
People people = new People();
// 任何一個類,都是java.lang.Class的實例對象
}
}
// People自己就是一個對象,是java.lang.Class的對象
class People { }
複製代碼
Class類的三種表達方式android
Class c1 = People.class;
// 方法2:經過已知類對象的getClass方法
Class c2 = people.getClass();
// 方法3:經過Class類靜態方法forName()
Class c3 = Class.forName("com.nj.reflect.People");
/* * c一、c2表示了People類的類類型(class type) * 類也是對象,是Class類的實例對象,這個對象咱們稱爲該類的類類型 */
// 無論是c一、c2仍是c3都表明People的類類型,一個類只多是Class類的一個實例對象。
System.out.print(c1 == c2); // true
System.out.print(c1 == c3); // true
複製代碼
獲取對象實例git
// 經過People類的類類型c一、c2或c3建立該類的實例對象。
People people1 = (People) c1.newInstance(); // 須要有無參構造方法
複製代碼
Class.forName("類的全稱")github
靜態加載類數組
public class Animal {
public static void main(String[] args) {
if (args[0].equals("Dog")) {
Dog dog = new Dog();
dog.eat();
}
if (args[0].equals("Cat")) {
Cat cat = new Cat();
cat.eat();
}
}
}
class Dog {
public void eat() { }
}
class Cat {
public void eat() { }
}
複製代碼
靜態加載類的缺點ide
1.若是Dog或者Cat不存在,會報錯。可是Dog和Cat不必定會使用。函數
2.經過new建立對象是靜態加載類,在編譯時刻,就須要加載全部可能使用到的類。spa
動態加載類code
經過Class.forName()動態加載類,就不會在編譯時報錯了。對象
public class AnimalManager {
public static void main(String[] args){
try {
// 動態加載類,在運行時刻加載。編譯不會再報錯。
Class c = Class.forName(args[0]);
// 建立對象,經過類類型建立該類的對象
Object o = c.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
複製代碼
可是咱們經過c.newInstance()建立類對象的時候,問題來了,是該轉換成Dog,仍是該轉換成Cat。因此,須要統一標準,建立Animals接口,使Dog和Cat都實現Animals接口。
public class AnimalManager {
public static void main(String[] args){
try {
// 動態加載類,在運行時刻加載。編譯不會再報錯。
Class c = Class.forName(args[0]);
// 建立對象,經過類類型建立該類的對象
Animals animals = (Animals) c.newInstance();
animals.eat();
} catch (Exception e) {
e.printStackTrace();
}
}
}
interface Animals{
void eat();
}
class Dog implements Animals{
@Override
public void eat() {}
}
class Cat implements Animals{
@Override
public void eat() {}
}
複製代碼
優勢
1.動態加載,更加靈活
2.方便拓展,若是想要加個Pig類,直接建立Pig類,實現Animals接口和方法便可,無需再修改AnimalManager類。
獲取class對象的相關信息
1.獲取class對象的成員變量
// 獲取class1對象的全部成員變量,不包括父類的成員變量
Field[] allFields = class1.getDeclaredFields();
// 獲取class1對象的public成員變量,包括父類裏面的成員變量
Field[] publicFields = class1.getFields();
// 獲取class1對象指定的成員變量,父類的沒法獲取
Field name1 = class1.getDeclaredField("name");
// 獲取class1對象指定的public成員變量,包括查找父類
Field name2 = class1.getField("name");
// 獲取變量的名稱
String name = field.getName();
// 獲取變量的類型
Class type = field.getType();
複製代碼
2.獲取class對象的方法
// 獲取class1對象的全部方法,不包括父類的方法
Method[] allMethods = class1.getDeclaredMethods();
// 獲取class1對象的public方法,包括父類的public方法
Method[] publicMethods = class1.getMethods();
// 獲取class1對象指定的方法,父類的沒法獲取
Method method1 = class1.getDeclaredMethod("getName");
// 獲取class1對象指定的方法,而且有一個int類型參數的方法
Method method2 = class1.getDeclaredMethod("getName", int.class);
// 獲取class1對象有兩個參數的指定方法
Method method3 = class1.getDeclaredMethod("getName", int.class, String.class);
// 獲取class1對象指定的public方法,包括父類的public方法。
Method method4 = class1.getMethod("getName");
複製代碼
3.獲取class對象的構造函數
// 獲取class1對象全部的構造函數
Constructor[] allConstructors = class1.getDeclaredConstructors();
// 獲取class1對象public的構造函數
Constructor[] publicConstructors = class1.getConstructors();
// 獲取class1對象指定參數的構造函數
Constructor constructor1 = class1.getDeclaredConstructor(int.class);
// 獲取class1對象指定參數而且爲public的構造函數
Constructor constructor2 = class1.getConstructor(int.class, String.class);
複製代碼
4.class對象的其餘方法
Annotation[] annotations = class1.getAnnotations();// 獲取class對象的全部註解
Annotation annotation = class1.getAnnotation(Override.class);// 獲取class對象指定註解
Class superclass = class1.getSuperclass(); // 獲取class對象直接父類的類對象
Type genericSuperclass = class1.getGenericSuperclass(); // 獲取class對象的直接父類的Type
Type[] genericInterfaces = class1.getGenericInterfaces(); // 獲取class對象全部接口的Type集合
String name = class1.getName(); // 獲取class對象名稱,包括報名路徑
String simpleName = class1.getSimpleName(); // 獲取class類名
Package aPackage = class1.getPackage(); // 獲取class包信息
boolean annotation = class1.isAnnotation();// 判斷是否爲註解類
boolean anEnum = class1.isEnum();// 判斷是否爲枚舉
boolean anInterface = class1.isInterface();// 判斷是否爲接口類
boolean array = class1.isArray();//判斷是否爲集合類型
boolean primitive = class1.isPrimitive(); // 判斷是否爲原始類型(8個基礎類型)
boolean anonymousClass = class1.isAnonymousClass(); // 判斷是不是匿名內部類
boolean annotation1 = class1.isAnnotationPresent(Override.class);//判斷是否被某個註解類修飾
複製代碼
5.Method的相關方法
Method method = class1.getMethod("getName", String.class);
String name = method.getName(); // 獲取方法名稱
// 獲取方法的返回值類型的類類型
Class returnType = method.getReturnType();
// 獲取方法的參數列表類型的類類型的數組
Class[] parameterTypes = method.getParameterTypes();
複製代碼
Java反射的使用
1.生成來的實例對象
// 方法一:調用newInstance()方法,可是這個方法要求class1的類必須有無參構造函數。
Dog dog = (Dog) class1.newInstance();
// 方法二:經過getConstructor獲取有參的Constructor構造函數,再調用newInstance()建立實例
Constructor constructor = class1.getConstructor(String.class);
Dog dog1 = (Dog) constructor.newInstance("dog");
複製代碼
2.調用類的方法
// 第一步:生成類對象
Dog dog = (Dog) class1.newInstance();
// 第二步:經過class對象的getMethod()等方法,得到具體須要執行的Method對象。
Method method = class1.getDeclaredMethod("getName", String.class);
// 若是方法是private修飾,直接調用invoke,會報錯,Java要求程序必須有調用該方法的權限。
// 調用setAccessible,並將參數設置爲true,就會取消Java語言的訪問權限檢查。
method.setAccessible(true);
// 調用Method對象的invoke方法,第一個參數是調用該方法的實例對象,第二個參數是對應須要傳入的參數。
// invoke的返回值:若是方法的返回值爲void,則invoke返回null,若是不是void,則返回具體的返回值
// 至關於 dog.getName("dog");
Object o = method.invoke(dog, "dog");
複製代碼
3.訪問成員變量的值
// 第一步:生成類對象
Dog dog = (Dog) class1.newInstance();
// 第二步:經過getDeclaredField等方法,獲取相應的Field對象
Field field = class1.getDeclaredField("age");
// 取消Java權限檢查
field.setAccessible(true);
// 獲取dog對象中age成員變量的值
int age = field.getInt(dog);
// 修改dog對象中age成員變量的值
field.setInt(dog, 25);
// 獲取dog對象中String類型的成員變量
Field field1 = class1.getDeclaredField("name");
// 取消Java權限檢查
field1.setAccessible(true);
// 獲取name變量的值
Object o = field1.get(dog);
// 修改name變量的值
field1.set(dog, "sam");
複製代碼
參考資料
1.Java反射