第七章
- 反射机制概述
- Class
- Field
- Method
- Constructor
反射机制概述
- 反射机制有什么用?
通过java语言中的反射机制可以操作字节码文件。
类似于黑客,可以读和改字节码文件。
-
反射机制的相关类在哪个包下?
java.lang.reflect.*;
-
反射机制相关的重要的类有哪些?
java.lang.Class;//代表字节码文件 java.lang.reflect.Method;//代表字节码中的方法字节码 java.lang.reflect.Constructor;//代表字节码中的构造方法字节码 java.lang.reflect.Field;//代表字节码中的属性字节码
获取Class的三种方式
/**
* 要操作一个类的字节码,需要首先获取到这个类的字节码,怎么获取java.lang.Class实例?
* 三种方式:
* 第一种:Class c = Class.forName("完整类名带包名")
* 第二种:Class c = 引用.getClass();
* 第三种:Class c = String.class;
* Class c = int.class;
*
*/
public class ReflectTest01 {
public static void main(String[] args) {
/*
Class.forName()
1、静态方法。
2、方法的参数是一个字符串。
3、字符串需要的是一个完整的类名。
4、完整类名必须带有包名。java.lang包也不能省略。
*/
Class c1=null;
Class c2=null;
//第一种方式
try {
c1 = Class.forName("java.lang.String");
c2 = Class.forName("java.util.Date");
Class c3 = Class.forName("java.lang.Integer");
Class c4 = Class.forName("java.lang.System");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//第二种方式
//java中任何一个对象都有一个方法getClass()
String s = "abd";
Class x = s.getClass();
System.out.println(c1==x);//true 内存地址一样
Date time = new Date();
Class y = time.getClass();
System.out.println(y==c2);//true
//第三种方式
Class z = String.class;
Class k = int.class;
Class f = Date.class;
Class e = double.class;
}
}
Class作用
/**
* 获取到class能干什么?
* 通过Class的newInstance()方法来实例化对象,
* 注意:newInstance()方法内部实际上调用了无参数构造方法,必须保证无参数构造方法的存在才可以。
*/
public class ReflectTest02 {
public static void main(String[] args) {
try {
//通过反射机制,获取Class,通过Class来创建对象。
Class c = Class.forName("com.chinasoft.reflect.User");
//也可以使用泛型。
Class<User> c2 = (Class<User>)Class.forName("com.chinasoft.reflect.User");
//newInstance方法会调用User类的无参构造方法。
Object obj = c.newInstance();
System.out.println(obj);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
}
/*
运行结果:
com.chinasoft.reflect.User@1b6d3586
*/
反射机制的灵活性
classinfo.properties文件:
className=com.chinasoft.reflect.User
#className=java.util.Date
/**
* 验证反射机制的灵活性。 传统的new方式就写死了。
* 这里只需要改classinfo.propertie文件的className即可创建不同类型的对象。
*/
public class ReflectTest03 {
public static void main(String[] args) throws Exception {
//通过IO流读取classinfo文件的className
FileReader reader = new FileReader("chapter25/src/classinfo.properties");
Properties p = new Properties();
p.load(reader);
//Set<Map.Entry<Object, Object>> set = p.entrySet();
String className = p.getProperty("className");
System.out.println(className);//com.chinasoft.reflect.User
Class c = Class.forName(className);
Object obj = c.newInstance();//通过反射机制创建对象
System.out.println(obj);
reader.close();//关闭流
}
}
/*
运行结果:
com.chinasoft.reflect.User
com.chinasoft.reflect.User@1b6d3586
*/
/**
* 研究一下Class.forName()发生了什么?
* 重点:
* 如果你只希望一个类的静态代码块执行,其他代码一律不执行
* 可以使用:
* Class.forName("完整类名");
*
* JDBC技术使用。
*/
public class ReflectTest04 {
public static void main(String[] args) {
try {
//这个方法的执行会导致类加载
Class.forName("com.chinasoft.reflect.MyClass");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
class MyClass{
//静态代码块在类加载时执行,并且只执行一次。
static {
System.out.println("静态代码块执行!");
}
}
获取类路径下的绝对路径(重要)
/**
* 获取类路径下的绝对路径。(这种方式是通用的!)
*/
public class ReflectTest05 {
public static void main(String[] args) {
//获取绝对路径的类必须在src根路径下。 "classinfo.properties"是在src根路径下开始的。
String path = Thread.currentThread().getContextClassLoader()
.getResource("classinfo.properties").getPath();
System.out.println(path);// /F:/IdeaProjects/javase/out/production/chapter25/classinfo.properties
}
}
对以往获取路径方式的改进(以后都用这种!!!)
/**
*IoProperties改进
*/
public class IoPropertiesTest {
public static void main(String[] args) throws Exception {
//获取classinfo.properties的绝对路径
String path = Thread.currentThread().getContextClassLoader()
.getResource("classinfo.properties").getPath();
FileReader reader = new FileReader(path);
Properties p = new Properties();
p.load(reader);
reader.close();
//通过key值获取value
System.out.println(p.getProperty("className"));//com.chinasoft.reflect.User
}
}
资源绑定器
这种方式可以代替IoProperties的方式!!!
/**
* java.util报下的一个资源绑定器,便于获取属性配置文件的内容。
* 使用以下这种方式的时候,属性配置文件xxx.properties必须放到类路径下。
*
* 这种方式可以代替IoProperties的方式!!!
*/
public class ResourceBundleTest {
public static void main(String[] args) {
//资源绑定器
//路径后面的扩展名不能写。
ResourceBundle rb = ResourceBundle.getBundle("classinfo2");
String className = rb.getString("className");
System.out.println(className);
}
}
Field
/**
* 反射Student类当中所有的Field
*/
public class ReflectTest06 {
public static void main(String[] args) throws ClassNotFoundException {
//获取整个类
Class stuClass = Class.forName("com.chinasoft.reflect.Student");
System.out.println(stuClass.getName());//com.chinasoft.reflect.Student
System.out.println(stuClass.getSimpleName());//Student
//获取类中所有的public修饰的Field
Field[] fields = stuClass.getFields();
System.out.println(fields.length);//1
for (int i=0;i<fields.length;i++){
System.out.println(fields[i].getName());
}
System.out.println("================================================");
//获取所有的Field
Field[] fields1 = stuClass.getDeclaredFields();
System.out.println(fields1.length);//4
for (Field field:fields1){
System.out.println(field.getName());//获取属性名
System.out.println(field.getType());//获取属性类型
int i = field.getModifiers();//获取属性修饰符
System.out.println(i);
String modifierString = Modifier.toString(i);
System.out.println(modifierString);
}
}
}
反编译一个类的属性Field(了解)
/**
*通过反射机制,反编译一个类的属性Field
*/
public class ReflectTest07 {
public static void main(String[] args) throws Exception{
//创建这个是为了拼接字符串。
StringBuilder s = new StringBuilder();
Class stuClass = Class.forName("com.chinasoft.reflect.Student");//java.lang.Integer
s.append(Modifier.toString(stuClass.getModifiers())+" class "+stuClass.getSimpleName()+" {\n");
Field[] fields = stuClass.getDeclaredFields();
for (Field field:fields){
s.append(Modifier.toString(field.getModifiers())+" "+field.getType().getSimpleName()+" "+field.getName()+";\n");
}
s.append("}");
System.out.println(s);
}
}
/*
运行结果:
public class Student {
public int no;
private String name;
protected int age;
boolean sex;
public static final double PI;
}
*/
通过反射机制访问对象属性(重点)
重点
/**
* 怎么通过反射机制访问一个java对象的属性?
* 给属性赋值set
* 获取属性的值get
*/
public class ReflectTest08 {
public static void main(String[] args) throws Exception{
Class stuClass = Class.forName("com.chinasoft.reflect.Student");
Object obj = stuClass.newInstance();
//根据属性的名称获取Field
Field field = stuClass.getDeclaredField("no");
//可以访问私有的属性吗?
Field nameField = stuClass.getDeclaredField("name");
//打破封装
nameField.setAccessible(true);//这样设置后,私有属性就可以访问了!
nameField.set(obj, "jackon");//私有属性不能直接访问
System.out.println(nameField.get(obj));//jackon
//给obj对象的no属性赋值
field.set(obj, 1111);
//读取属性的值
Object o = field.get(obj);
System.out.println(o);//1111
}
}
/*
运行如下:
jackon
1111
*/
Method(最重要)
可变长度参数:
/**
* 可变长度参数
* int... args 这是可变长度参数
* 语法是:类型... (注意:一定是3个点)
*
* 1、可变长度参数要求参数的个数是:0~N个。
* 2、可变长度参数在参数列表中必须在最后一个位置上。
*/
public class ArgsTest {
public static void main(String[] args) {
m();
m(10);
m(10,100);
m2(5);
m2(5,"100");
m2(5,"100","200");
//也可以传一个数组
String[] strs = {"a","b","c"};
m3(strs);
}
public static void m3(String... args) {
}
public static void m2(int a,String... args) {
//可以将可变长度参数当做一个数组来看。
for (int i=0;i<args.length;i++){
System.out.println(args[i]);
}
}
public static void m(int... args) {
System.out.println("m方法执行了!");
}
}
通过反射机制调用方法 重点!!!
重点
/**
* 重点(必须掌握):通过反射机制调用方法。
*
* 要素分析:
* 要素1:对象userService
* 要素2:login方法名
* 要素3:实际参列表
* 要素4:返回值
*/
public class ReflectTest09 {
public static void main(String[] args) throws Exception{
Class c = Class.forName("com.chinasoft.reflect.UserService");
Object obj = c.newInstance();
Method loginMethod = c.getDeclaredMethod("login", String.class,String.class);//获取login方法
//反射机制中最最最重要的方法 invoke()
Object ret = loginMethod.invoke(obj, "admin","123");//执行对象obj的login方法
System.out.println(ret);//true
}
}
Constructor
通过反射机制调用构造方法:
/**
* 通过反射机制调用构造方法
*/
public class ReflectTest10 {
public static void main(String[] args) throws Exception{
Class vipClass = Class.forName("com.chinasoft.reflect.Vip");
//调用无参数构造方法创建对象
Object obj = vipClass.newInstance();
//调用有参数构造方法怎么办?
//第一步:先获取到这个有参数的构造方法
Constructor constructor = vipClass.getConstructor(int.class,String.class,String.class,boolean.class);
//第二步:调用构造方法创建对象
Object obj2 = constructor.newInstance(1,"王磊","1999-11-29",true);
//获取无参构造方法
Constructor constructor1 = vipClass.getDeclaredConstructor();
Object obj3 = constructor1.newInstance();
System.out.println(obj);
System.out.println(obj2);
System.out.println(obj3);
}
}
/*
运行结果:
Vip{no=0, name='null', birth='null', sex=false}
Vip{no=1, name='王磊', birth='1999-11-29', sex=true}
Vip{no=0, name='null', birth='null', sex=false}
*/
通过获取父类和父接口
/**
* 获取父类和父接口
*/
public class ReflectTest11 {
public static void main(String[] args) throws Exception{
//String
Class stringClass = Class.forName("java.lang.String");
//获取String类的父类
Class superClass = stringClass.getSuperclass();
System.out.println(superClass.getName());
System.out.println("===================================");
//获取String类实现的所有接口(一个类可以实现多个接口)
Class[] interfaces = stringClass.getInterfaces();
for (Class in:interfaces){
System.out.println(in.getName());
}
}
}