目 录
反射机制有什么用?
通过java语言中的反射机制可以操作字节码文件,让代码更具通用性(更灵活),可变化的内容都是写到配置文件当中,将来修改配置文件文件之后,创建的对象不一样了,调用的方法也不同,但是java代码不需要要做任何改动。
反射机制的优缺点:
优点: 运行期类型的判断,动态加载类,提高代码灵活度。
缺点: 性能瓶颈:反射相当于一系列解释操作,通知 JVM 要做的事情,性能比直接的java代码要慢很多。
反射机智的相关类在哪个包下?
java.lang.Reflect.*;
反射机制相关的类有哪些?(重要的类)
java.lang.class 代表整个字节码
java.lang.reflect.Method 代表字节码中的方法字节码
java.lang.reflect.Constructor 代表字节码中的构造方法字节码
java.lang/reflect.Field 代表字节码中的属性字节码
获取一个类的字节码class
获取一个类的字节码有三种方法:
Class c = Class.forName("");
Class c = 引用.getClass();
Class c = 任何类型.class
获取Class(类)
获取类的三种方法,代码实例:
public class ReflectTest01 {
public static void main(String[] args){
Class c1=null;
/*
第一种方法
Class.forName()
1.静态方法
2.参数是一个字符串
3.字符串需要的是一个完整类名
*/
try {
//c1代表String.class文件(或者说代表String类型)
c1=Class.forName("java.lang.String");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
/*
第二种方法
java中任何一个对象都有一个方法:getClass()
*/
String s="abc";
//x代表String.class字节码文件,x代表String类型
Class x=s.getClass();
System.out.println(c1==x);//true
/*
第三种方法
java语言中任何一种类型,都有.class属性
*/
//a代表String类型
Class y= String.class;//a代表String类型
System.out.println(x==y);//true
}
}
通过Class的newInstance()方法来实例化对象
注意:newInstance()方法内部实际上调用了无参构造方法,必须保证无参构造存在才行
代码实例:
创建User类:
public class User {
public User() {
System.out.println("无参数构造方法执行");
}
}
测试类:
public class ReflectTest02 {
public static void main(String[] args) {
try {
//通过反射机制,获取Class,通过Class来实例化对象
Class c=Class.forName("bean.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();
}
}
}
无参数构造方法执行
bean.User@14ae5a5
Tip:如果只希望一个类的静态代码块执行,其他代码不执行可以使用:
class.forName(“完整类名”)
该方法执行会导致类加载,静态代码块在类加载时执行
通过反射机制访问对象的属性Field
创建Student类
//反射属性field
public class Student {
public int no;
}
测试类
注意:给对象的某个属性赋值的三要素:对象、属性、实参
public class ReflectTest06 {
public static void main(String[] args) throws Exception{
Class thisClass=Class.forName("bean.Student");
//实例化一个对象(无参构造)
Object obj = thisClass.newInstance();
//获取某个属性no
Field noFiled = thisClass.getDeclaredField("no");
//给obj对象的no赋值
//三要素:对象、属性、实参
noFiled.set(obj,2222);
System.out.println(noFiled.get(obj));
}
}
通过反射机制访问对象的方法Method
创建UserService类
public class UserService {
public boolean login(String name,String password){
if ("admin".equals(name)&&"123".equals(password)){
return true;
}
return false;
}
public void logout(){
System.out.println("成功退出");
}
}
创建测试类
注意:调用某个对象的方法的四要素:对象、方法名、返回值、实参
import java.lang.reflect.Method;
/*
重点:通过反射机制怎么调用一个对象的方法
*/
public class ReflectTest07 {
public static void main(String[] args) throws Exception{
Class userServiceClass=Class.forName("bean.UserService");
//实例化对象
Object obj=userServiceClass.newInstance();
//获取方法(方法名+参数)
Method method=userServiceClass.getDeclaredMethod(
"login",String.class,String.class);
//调用方法
//四要素:对象、方法名、返回值、实参
Object returunMethod=method.invoke(obj,"admin","123");
System.out.println(returunMethod);//true
}
}
通过反射机制调用一个对象的构造方法Constructor
创建Vip类
public class Vip {
String name;
String password;
public Vip() {
}
public Vip(String name, String password) {
this.name = name;
this.password = password;
}
@Override
public String toString() {
return "Vip{" +
"name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
}
创建测试类:
import java.lang.reflect.Constructor;
/*
重点:通过反射机制怎么调用一个对象的构造方法实例化对象
*/
public class ReflectTest08 {
public static void main(String[] args) throws Exception{
Class c=Class.forName("bean.Vip");
//获取构造方法
Constructor con=c.getConstructor(String.class,String.class);
//调用构造方法new对象
Object newObj=con.newInstance("a123","123");
System.out.println(newObj);
}
}
获取一个类的父类,和所有接口
一个类的父类只有一个,接口可能有多个
所以获取接口返回的是个数组
public class ReflectTest09 {
public static void main(String[] args) throws Exception{
Class c=Class.forName("java.lang.String");
//获取父类
System.out.println(c.getSuperclass());
//获取接口
Class[] classes=c.getInterfaces();
for(Class in:classes){
System.out.println(in.getName());
}
}
}
关于通用的获取文件绝对路径
前提:文件必须在类路径下
import java.io.InputStream;
import java.util.Properties;
public class AboutPath {
public static void main(String[] args) throws Exception {
//这种方式获取文件绝对路径是通用的
//以下通用方式的前提是:必须在同一个类的根路径下(src)
String path=Thread.currentThread()
.getContextClassLoader()
.getResource("bean\\classinfo.properties")
.getPath();
//或者直接以流的形式返回
InputStream path2=Thread.currentThread()
.getContextClassLoader()
.getResourceAsStream("bean\\classinfo.properties");
System.out.println(path);
System.out.println(path2);
}
}
关于资源绑定器
java.util包下提供了一个资源绑定器,便于获取【属性配置文件】中的内容。
使用以下方法,属性配置文件必须放在类路径下
创建属性配置文件:classinfo.properties
className=bean.User
测试类
import java.util.ResourceBundle;
public class ResourceBundleTest {
public static void main(String[] args){
//只能绑定.properties文件
//路径扩展名省略
ResourceBundle bundle=ResourceBundle.getBundle("bean\\classinfo");
String className=bundle.getString("className");
System.out.println(className);//bean.User
}
}