Class
在面向对象中,万物皆对象,我们把人、动物等都抽象成类,将类具体化就成了对象,比如new Studen() 学生是个类,类里面指定的某一个学生就是研究的对象。
在java中类用class来表示
比如定义一个类:
public class Studen {
}
万物抽象可以用class来表示,那么class是不是也属于一个类型呢,所以java将所有的class都用一个类型(Class)来表示,Class(注意大小写)就是用来表示类的类型
就像Studen属于class,那么Student虽在的类的类型用Class表示,一句话,Class是所有类的类型,包括Object是一个class,Object对应的类的类型是Class
笔者认为也可以理解为Class是所有类的字节码对象。负责管理字节码的。
java的原文件以.java为后缀,编译后的字节码以.class为后缀
怎么创建一个类?
在java里创建类的常规方式是通过new这个关键字来创建的。
Studen studen = new Studen();
还可以通过Class来创建,
每个.class文件都必须先由JVM加载到内存,反射中只要能读取到这个.class文件就能创建一个对象。
java中有三种方式可以获得自己的Class
//1.通过类名.class
Class clazz1 = Studen.class;
//2.通过对象引用.getClass()
Class clazz2 = studen.getClass();
//3.通过类的完整路劲名
Class clazz3 = Class.forName("com.jiuletech.app.Studen");
这里clazz1 clazz2 clazz3都是Class的对象,完全相同
System.out.println(clazz1 == clazz2);
System.out.println(clazz2== clazz3);
可以看到控制台打印两个true,说明都是同一个Class,即上面说的同一份字节码,就算你创建N个对象,内存中依旧只有一份字节码,上面三个Class指向的都是同一份字节码,因此为true
得到了Class类类型,就可以通过它来创建对象
修改Student,添加方法printName()
public void printName(String name){
System.out.println("以后请叫我的外国名字:"+name);
}
演示反射调用方法
Class clz = null;
try {
clz = Class.forName("com.jiuletech.app.Student");
Student o = (Student) clz.newInstance();
Method method = clz.getMethod("printName", String.class);
method.invoke(o, "沃德天·维森莫·拉莫帅·帅德布耀德");
} catch (Exception e) {
e.printStackTrace();
}
从上面的代码我们可以看到核心的四步:
- 通过Class.forName("完整路劲名")获取到Class对象
- 调用Class的newInstance()方法
这个方法会将上一步加载的字节码实例化出来,相当于平时的new - 通过getMethod("方法名","方法参数类型")获取到对象中的方法
- 调用invoke("哪个对象","方法参数")执行Student里面的printName方法。
通过简单的四步,在只知道一个对象完整路劲名的情况下就可以随意玩弄这个对象。
体验了反射的强大之处在来扩展反射常用的API
反射之-构造函数
Student添加一个参数的构造函数
public Student(String name) {
this.name = name;
System.out.println("构造函数执行:"+name);
}
这里我们再用上面的clz.newInstance()就会报错,因为定义了一个有参构造函数,默认的无参构造函数就没有了,就会早不到这个方法。
怎么调用有参的构造函数呢?
try {
Constructor constructor = clz.getConstructor(String.class);
Object o = constructor.newInstance("我的外国名字");
} catch (Exception e) {
e.printStackTrace();
}
通过getConstructor()传入参数类型,得到这个构造函数,然后调用newInstance()传入参数,就会创建这个对象,构造函数就会首先执行。
看看打印的日志是不是这样?
其他API介绍
首先修改Student类:
/**
* Created by on 2017/10/31.
*/
public class Student {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void printName(String name){
System.out.println("以后请叫我的外国名字:"+name);
}
private void printAge(int age){
System.out.println("我今年"+age+"岁了");
}
}
getMethods()测试
Method[] methods = clz.getMethods();
for (Method method : methods) {
System.out.println(method.getName());
}
通过日志可以看出getMethods()获取了Student中除了私有方法外的所有方法,包括父类的
getDeclaredMethods()测试
Method[] declaredMethods = clz.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod.getName());
}
通过上述日志可以看到getDeclaredMethods()获取了Student中所有方法,包括私有方法,但不包括父类中的方法
细心的读者会看到无论是clz.getMethods()还是getDeclaredMethods()都对应有通过参数得到对应的某个方法
例如:得到方法名为setAge,参数类型为int.class的方法
Student o = (Student) clz.newInstance();
Method getAge = clz.getMethod("setAge",int.class);
getAge.invoke(o,23);
System.out.println(o.getAge());
最后通过打印日志可以看出setAge传入的年龄23生效
查看getMethod(...)源码
第一参数name需要传入指定方法名称,后面parameterTypes是一个可变参数,按参数的类型和顺序依次传参,getDeclaredMethod(...)方法使用类似,可自行动手练习
获取属性
Student修如下
public class Student {
private String name;
private int age;
protected String hobby;
public String specialTalents;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void printName(String name){
System.out.println("以后请叫我的外国名字:"+name);
}
private void printAge(int age){
System.out.println("我今年"+age+"岁了");
}
protected void testProtected(){
System.out.println("testProtected");
}
}
getFields()测试
Field[] fields = clz.getFields();
for (Field field : fields) {
System.out.println(field.getName());
}
通过运行结果可以看出,只有公有属性被获取到了
获取属性和获取方法类似,可以通过传入参数获取到对应的属性。读者可自行练习
怎么获取私有属性和方法
要获取私有的就调用带getDeclaredxxxx(),Declared的意思是申明的,公开的,需要注意的是getDeclaredxxxx()可以拿到所有私有的,能获取但不一定能访问。
Field[] fields = clz.getDeclaredFields();
for (Field field : fields) {
System.out.println(field.getName());
}
访问name属性
Object o = clz.newInstance();
Field[] fields = clz.getDeclaredFields();
for (Field field : fields) {
if(field.getName().equals("name")){
field.set(o,"我的天");
}
System.out.println(field.getName());
}
异常提示,不能使用私有属性
怎么获取私有属性?
Object o = clz.newInstance();
Field[] fields = clz.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
if(field.getName().equals("name")){
field.set(o,"我的天");
System.out.println(((Student)o).getName());
}
}
结果可以看到运行成功。
上面的代码只加了field.setAccessible(true);,表示可以访问这个私有属性,然后强转对象打印姓名,发现更改成功。私有方法的访问也是类似的。
关于通过反射怎么得到属性和方法就介绍到这里。
敬请期待下一篇深入理解Java反射(二)
谢谢!