2021.8.27
Class类
在Java里,出了int等基本类型外,其他类型都是Class
Class类在JVM是动态加载的,当第一次读取到class类时,会将其加载到内存当中
每加载一个类,都会创建一个叫Class的类的实例与其关联起来,在这个叫Class的类里,保存了该class的所有的信息,包括类名、包名、父类、实现的接口、所有方法、字段等,因此,如果获取了某个class
实例,我们就可以通过这个class实例获取到该实例对应的class的所有信息。
这种通过Class
实例获取class
信息的方法称为反射(Reflection)。
获取Class有三种手段
//通过一个class的静态变量class获取
Class cls = String .class;
//利用一个实例变量,从其的getClass方法获取
String s = "hello";
Class cls = s.getClass()
//如果知道一个class的完整类名,可以通过静态方法Class.forName()获取
Class cls = Class.forName("java.lang.String")
因为Class实例在JVM是唯一的,通过 == 可以比较两个Class实例
Class cls1 = String.class;
String s = "Hello";
Class cls2 = s.getClass();
boolean sameClass = cls1 == cls2; // true
【instanceof】不但匹配指定类型,还要匹配类型的子类
【 == 】:匹配制定类型,但不能做子类型的比较
Integer n = new Integer(123);
boolean b1 = n instanceof Integer; // true,因为n是Integer类型
boolean b2 = n instanceof Number; // true,因为n是Number类型的子类
boolean b3 = n.getClass() == Integer.class; // true,因为n.getClass()返回Integer.class
boolean b4 = n.getClass() == Number.class; // false,因为Integer.class!=Number.class
访问字段
通过Class实例获取字段信息:
- Field getField(name):根据字段名获取某个public的field(包括父类)
- Field getDeclaredField(name):根据字段名获取当前类的某个field(不包括父类)
- Field[] getFields():获取所有public的field(包括父类)
- Field[] getDeclaredFields():获取当前类的所有field(不包括父类)
public class Main {
public static void main(String[] args) throws Exception {
Class stdClass = Student.class;
// 获取public字段"score": public int Student.score
System.out.println(stdClass.getField("score"));
// 获取继承的public字段"name": public java.lang.String Person.name
System.out.println(stdClass.getField("name"));
// 获取private字段"grade": private int Student.grade
System.out.println(stdClass.getDeclaredField("grade"));
}
}
class Student extends Person {
public int score;
private int grade;
}
class Person {
public String name;
}
【field对象】:包含了一个字段的所有信息
getName()
:返回字段名称,例如,"name"
;getType()
:返回字段类型,也是一个Class
实例,例如,String.class
;getModifiers()
:返回字段的修饰符,它是一个int
,不同的bit表示不同的含义。
public final class String {
private final byte[] value;
}
Field f = String.class.getDeclaredField("value");
f.getName(); // "value"
f.getType(); // class [B 表示byte[]类型
int m = f.getModifiers();
Modifier.isFinal(m); // true
Modifier.isPublic(m); // false
Modifier.isProtected(m); // false
Modifier.isPrivate(m); // true
Modifier.isStatic(m); // false
【获取字段】
除了我们正常的访问字段,就比如说person.name,还可以利用反射拿到一个字段的Field是来访问.
import java.lang.reflect.Field;
public class Main{
public static void main(String[] args) throws Exception {
object p = new Person("wudibooo");
Class cls = p.getClass();
Field f = cls.getDeclaredField("name");
f.setAccessible(true); //设置许可
Object value = f.get(p);
System.out.println(value); // "Xiao Ming"
}
class Person {
private String name;
public Person(String name) {
this.name = name;
}
}
【设置字段】 :也可以利用反射来修改字段,先获取Class类,再获取字段Field,用set来修改
public class Main {
public static void main(String[] args) throws Exception {
Person p = new Person("Xiao Ming");
System.out.println(p.getName()); // "Xiao Ming"
Class c = p.getClass();
Field f = c.getDeclaredField("name");
f.setAccessible(true);
f.set(p, "Xiao Hong");
System.out.println(p.getName()); // "Xiao Hong"
}
}
class Person {
private String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
通过Field实例可以读取或设置某个对象的字段,如果存在访问限制,要首先调用setAccessible(true)
来访问非public
字段。
通过反射读写字段是一种非常规方法,它会破坏对象的封装。
调用方法
有点像如何访问字段的,通过Class实例获取所有Method信息,然后获取方法
Method getMethod(name, Class...)
:获取某个public
的Method
(包括父类)Method getDeclaredMethod(name, Class...)
:获取当前类的某个Method
(不包括父类)Method[] getMethods()
:获取所有public
的Method
(包括父类)Method[] getDeclaredMethods()
:获取当前类的所有Method
(不包括父类)public class Main { public static void main(String[] args) throws Exception { Class stdClass = Student.class; // 获取public方法getScore,参数为String: System.out.println(stdClass.getMethod("getScore", String.class)); // 获取继承的public方法getName,无参数: System.out.println(stdClass.getMethod("getName")); // 获取private方法getGrade,参数为int: System.out.println(stdClass.getDeclaredMethod("getGrade", int.class)); } } class Student extends Person { public int getScore(String type) { return 99; } private int getGrade(int year) { return 1; } } class Person { public String getName() { return "Person"; } }
一个Method
对象包含一个方法的所有信息:
getName()
:返回方法名称,例如:"getScore"
;getReturnType()
:返回方法返回值类型,也是一个Class实例,例如:String.class
;getParameterTypes()
:返回方法的参数类型,是一个Class数组,例如:{String.class, int.class}
;getModifiers()
:返回方法的修饰符,它是一个int
,不同的bit表示不同的含义。
【调用方法】
利用反射来调用方法,需要先实例化一个该方法的method对象,在用invoke来调用该方法,invoke后面的参数第一个要放入对于的实例,后面的参数类型也需要一致。
import java.lang.reflect.Method;
public class Main {
public static void main(String[] args) throws Exception {
// String对象:
String s = "Hello world";
// 获取String substring(int)方法,参数为int:
Method m = String.class.getMethod("substring", int.class);
// 在s对象上调用该方法并获取结果:
String r = (String) m.invoke(s, 6);
// 打印调用结果:
System.out.println(r);
}
}
//正常
String s = "Hello world";
String r = s.substring(6); // "world"
【调用静态方法】
静态方法无需实例化,所以invoke方法传入的第一个参数永远是null,以Interger.parseInt(string)
为例
import java.lang.reflect.Method;
public class Main {
public static void main(String[] args) throws Exception {
// 获取Integer.parseInt(String)方法,参数为String:
Method m = Integer.class.getMethod("parseInt", String.class);
// 调用该静态方法并获取结果:
Integer n = (Integer) m.invoke(null, "12345");
// 打印调用结果:
System.out.println(n);
}
}
【调用非public】 :需要设置许可
import java.lang.reflect.Method;
public class Main {
public static void main(String[] args) throws Exception {
Person p = new Person();
Method m = p.getClass().getDeclaredMethod("setName", String.class);
m.setAccessible(true);
m.invoke(p, "Bob");
System.out.println(p.name);
}
}
class Person {
String name;
private void setName(String name) {
this.name = name;
}
}
【多态的情况】
import java.lang.reflect.Method;
public class Main {
public static void main(String[] args) throws Exception {
// 获取Person的hello方法:
Method h = Person.class.getMethod("hello");
// 对Student实例调用hello方法:
h.invoke(new Student());
}
}
class Person {
public void hello() {
System.out.println("Person:hello");
}
}
class Student extends Person {
public void hello() {
System.out.println("Student:hello");
}
}
Student:hello
使用反射调用方法时,仍然遵循多态原则:即总是调用实际类型的覆写方法(如果存在)
调用构造方法
【newInstance】
使用class的newInstance来调用
Person p = Person.class.newInstance();
//正常用new
Person p = new Person()
利用反射调用构造方法,有很大的局限性,只能调用该类的public无参数的构造犯法,否者不可以。
【Constructor】
为了调用任意的构造方法,Java提供过了Constructor对象,与Method只有一个在调用构造方法上有不同,且返回结果总是返回实例。
import java.lang.reflect.Constructor;
public class Main {
public static void main(String[] args) throws Exception {
// 获取构造方法Integer(int):
Constructor cons1 = Integer.class.getConstructor(int.class);
// 调用构造方法:
Integer n1 = (Integer) cons1.newInstance(123);
System.out.println(n1);
// 获取构造方法Integer(String)
Constructor cons2 = Integer.class.getConstructor(String.class);
Integer n2 = (Integer) cons2.newInstance("456");
System.out.println(n2);
}
}
通过Class实例获取Constructor的方法如下:
getConstructor(Class...)
:获取某个public
的Constructor
;getDeclaredConstructor(Class...)
:获取某个Constructor
;getConstructors()
:获取所有public
的Constructor
;getDeclaredConstructors()
:获取所有Constructor
。
获取继承关系
通过Class
对象可以获取继承关系:
Class getSuperclass()
:获取父类类型;Class[] getInterfaces()
:获取当前类实现的所有接口。
【getsuperclass】
public class Main {
public static void main(String[] args) throws Exception {
Class i = Integer.class;
Class n = i.getSuperclass(); //class java.lang.Number
System.out.println(n);
Class o = n.getSuperclass(); //class java.lang.Object
System.out.println(o);
System.out.println(o.getSuperclass()); //null
}
}
Integer
的父类类型是Number
,Number
的父类是Object
,Object
的父类是null
。除Object
外,其他任何非interface
的Class
都必定存在一个父类类型
【getInterfaces】:只返回当前类直接实现的接口类型,并不包括其父类实现的接口类型,如果该类没有任何interface,则返回空数组
import java.lang.reflect.Method;
public class Main {
public static void main(String[] args) throws Exception {
Class s = Integer.class;
Class[] is = s.getInterfaces();
for (Class i : is) {
System.out.println(i);
}
}
}
--------------------
interface java.lang.Comparable
interface java.lang.constant.Constable
interface java.lang.constant.ConstantDesc
/
public class Main {
public static void main(String[] args) throws Exception {
Class s = Integer.class.getSuperclass(); //父类Number
Class[] is = s.getInterfaces();
for (Class i : is) {
System.out.println(i);
}
}
}
-------------------
interface java.io.Serializable
注意的是对接口调用superclass 总是返回null
【继承关系】:
判断一个实例是否是某个类型,我们都是使用Instanceof ,如果判断两个Class实例,要判断一个向上转型是否成立,可调用isAssignableFrom()
// Integer i = ?
Integer.class.isAssignableFrom(Integer.class); // true,因为Integer可以赋值给Integer
// Number n = ?
Number.class.isAssignableFrom(Integer.class); // true,因为Integer可以赋值给Number
// Object o = ?
Object.class.isAssignableFrom(Integer.class); // true,因为Integer可以赋值给Object
// Integer i = ?
Integer.class.isAssignableFrom(Number.class); // false,因为Number不能赋值给Integer
动态代理
感觉这里很深,不太明白,以后回来看