反射机制有什么用:
通过java语言中的反射机制可以读和修改字节码文件。
反射机制的相关类在哪个包下:java.lang.reflect.*;
反射机制相关的类有哪些:
java.lang.class; //代表整个字节码,代表一个类型。代表整个类。
java.lang.reflect.Method; //代表字节码中的方法字节码。代表类中的方法
java.lang.reflect.Constructor; //代表字节码中的构造方法字节码。代表类中的构造方法
java.lang.reflect.Field; //代表字节码中的属性字节码。代表类中的成员变量(静态变量+实例变量)
/*
* Class.forName()
* 1.静态方法
* 2.方法的参数是一个字符串
* 3.字符串需要的是一个完整类名
* 4.完整类名必须带有包名。java.lang包也不能省略。
* */
Class c1=Class.forName("java.lang.String"); //c1代表String类型
Class c2=Class.forName("java.util.Date"); //c2代表Date类型
Class c3=Class.forName("java.lang.Integer");//c3代表Integer类型
Class c4=Class.forName("java.lang.System");// c4代表System类型
/*
* 第二种方式:java中任何一个对象都有一个方法:getClass()
* */
String s="abc";
Class x=s.getClass(); //x代表String.class字节码文件。x代表String类型
System.out.println(c1==x);//true(==判断的是对象的内存地址)
/*
* 第三种方式:java语言中任何一个类型,包括基本数据类型,它都有class属性
* */
Class z=String.class; //z代表String类型
Class k= Date.class;//k代表Date类型
Class f=int.class;//f代表int类型
System.out.println(x==z); //true
/*
* 获取class的三种方式
* 第一种:Class c=Class.forName("完整类名加包名");
* 第二种:Class c=对象。getClass();
* 第三种:Class c=任何类型.class;
* */
idea获取类名:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AwoORetB-1596363357479)(C:\Users\user\Desktop\java分析图\获取类名.png)]
通过Class的newInstance()方法来实例化对象。newInstance()方法内部实际上调用了无参数构造方法,必须保证无参构造存在才可以。 不然会出现 ”实例化“ 异常。
public class User {
public User() {
System.out.println("无参数构造方法");
}
}
public class ReflectTest02 {
public static void main(String[] args) {
//通过反射机制获取class,通过Class来实例化对象
try {
Class c=Class.forName("com.ma.bean.User"); //c代表User对象
//newInstance() 这个方法会调用User这个类的无参数构造方法,完成对象的创建
//重点newInstance()调用的无参构造,必须保证无参构造是存在的
Object obj=c.newInstance();//com.ma.bean.User@282ba1e
System.out.println(obj);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
}
className=com.ma.bean.User
/*
*
* 验证反射机制的灵活性
* java代码写过一遍之后,在不改变java源代码的基础之上,可以做到不同对象的实例化
* 非常之灵活(符合OCP开闭原则)
* */
public class ReflectTest03 {
public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException {
//这种方式代码是写死的,只能创建一个User类型对象
//User u=new User();
//以下代码是灵活的,代码不需要改动,只需修改配置文件,就可以创建不同的实例对象
//通过io流,读取文件
FileReader reader=new FileReader("java反射/classinfo.properties");
//创建属性类对象
Properties pro=new Properties(); //key value都是String
//加载
pro.load(reader);
//关闭流
reader.close();
//通过key获取value
String className=pro.getProperty("className");
System.out.println(className); //com.ma.bean.User
//通过反射机制实例化对象
Class c=Class.forName(className);
System.out.println(c); //class com.ma.bean.User
Object obj=c.newInstance();
System.out.println(obj); //com.ma.bean.User@64c64813
}
}
Class.forName()发生了什么:
如果你只需要一个类的静态代码块执行,其他代码一律不执行,你可以使用Class.forName(“完整类型”),这个方法的执行会导致类加载,类加载时,静态代码执行,并且只执行一次。后面一些框架的原理与之相关。
代码演示:
/*
* 研究一下:Class.forName()发生了什么
* 重点:如果你只需要一个类的静态代码块执行,其他代码一律不执行,你可以使用
* Class.forName("完整类型"),这个方法的执行会导致类加载,类加载时,静态代码执行
* */
public class ReflectTest04 {
public static void main(String[] args) {
try {
//Class.forName()这个方法的执行会导致类加载
Class.forName("com.ma.reflect.Myclass");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
class Myclass{
// 静态代码块在类加载时执行,并且只执行一次
static{
System.out.println("MyClass类的静态代码块执行了");
}
}
FileReader reader=new FileReader("java反射/classinfo.properties");
移植性差,在IDEA中默认的当前路径是project的根,这个代码假设离开了IDEA,换到了其他位置,可能当前路径就不是project的根了,这时这个路径就无效了。
/*
* 解释:
* Thread.currentThread() :当前线程对象
* getContextClassLoader():是线程对象的方法,可以获取到当前线程的类加载器对象。
* getResource():【获取资源】这是类加载器的方法,当前线程的类加载器默认从类的根路径下加载资源。
* getPath():路径
*/
String path=Thread.currentThread().getContextClassLoader().
getResource("className.properties").getPath();
System.out.println(path);//D:/javase进阶/out/production/java反射/className.properties
//获取db.properties文件下的绝对路径。
String path1=Thread.currentThread().getContextClassLoader().
getResource("com/ma/bean/db.properties").getPath();
System.out.println(path1); //D:/javase进阶/out/production/java反射/com/ma/bean/db.properties
public class IoPropertiesTest {
public static void main(String[] args) throws IOException {
/* String path=Thread.currentThread().getContextClassLoader().
getResource("className.properties").getPath();
FileReader reader=new FileReader(path);*/
//直接以流的形式返回
InputStream reader=Thread.currentThread().getContextClassLoader().
getResourceAsStream("className.properties");
Properties pro=new Properties();
pro.load(reader);
reader.close();
//通过key获取value
String className=pro.getProperty("className");
System.out.println(className); //com.ma.bean.User
}
}
资源绑定器(ResourceBundle):
java.util包下提供了一个资源绑定器,便于获取属性配置文件中的内容。使用以下这种方式的时候,文件扩展名必须是properties,属性配置文件xxx.properties必须放到类路径下,在写路径的时候,路径后面的扩展名不用写(.properties)。
代码如下:
/*
* java.util包下提供了一个资源绑定器,便于获取属性配置文件中的内容。
* 使用以下这种方式的时候,属性配置文件xxx.properties必须放到类路径下。
* */
public class ResourceBundleTest {
public static void main(String[] args) {
//资源绑定器,只能绑定xxx.properties文件,并且这个文件必须在类路径下。
//在写路径的时候,路径后面的扩展名不能写。
ResourceBundle bundle=ResourceBundle.getBundle("className");
String className=bundle.getString("className");
System.out.println(className);
}
}
什么是类加载器:
专门负责加载类的命令或工具。 ClassLoader。
JDK中自带了3个类加载器:
假设有这样的一段代码:
String s="abc";
代码在开始执行之前,会将所需要的类全部加载到JVM当中。通过类加载器加载,看到以上代码,类加载器会找String.class文件,找到就加载。首先通过 “启动类加载器” 加载。 启动类加载器专门加载 ” jre\lib\rt.jar“,rt,jar中都是JDK最核心的类库。
如果通过 ”启动类加载器“ 加载不到的时候,会通过 ”扩展类加载器“ 加载。 扩展类加载器专门加载 ”jre\lib\ext*.jar“ 。
如果通过 “扩展类加载器” 加载不到的时候,会通过 “应用类加载器” 加载。 应用类加载器专门加载 classpath中的jar包(class文件)。
String getName(); //获取完整类名
String getSimpleName(); //获取简类名
Field[] getFields(); //获取类中所有的public修饰的Field
Field[] getDeclaredFields(); //获取所有的Field
int getModifiers(); //获取属性修饰符,返回的修饰符是一个数字,每个数字是修饰符的代号。
String toString();//将修饰符代号数字转化为字符串
Class<?> getType(); //获取属性的类型
String getName(); //获取属性的名字
// 反射属性Field
public class Student {
//Field 翻译为字段 其实就是属性/成员
// 4个Field 分别采用了不同的访问控制权限修饰符
public int no; //public int no 整个Field对象。
private String name;
protected int age;
boolean sex;
public static final double MATH_PI=3.1415926;
}
/*
* 反射Student类当中所有的 Field
* */
public class ReflectTest05 {
public static void main(String[] args) throws ClassNotFoundException {
// 获取整个类
Class studentClass=Class.forName("com.ma.bean.Student");
String className=studentClass.getName();
System.out.println("完整类名:"+className); //com.ma.bean.Student
String simpleName=studentClass.getSimpleName();
System.out.println("简类名:"+simpleName); //Student
// 获取类中所有的public修饰的Field Field[] getFields()
Field[]fields= studentClass.getFields();
System.out.println(fields.length); //1
//取出这个元素
Field f=fields[0];
//取出这个Field它的名字
String fieldName=f.getName();
System.out.println(fieldName); //no
// 获取所有的Field
Field[] fs=studentClass.getDeclaredFields();
System.out.println(fs.length); //4
System.out.println("===============================");
//遍历
for (Field field:
fs) {
//获取属性修饰符
int i=field.getModifiers(); //1 返回的修饰符是一个数字,每个数字是修饰符的代号。
//如何将这个代号数字转化为字符串
String modifierString=Modifier.toString(i);
System.out.println(modifierString);
// 获取属性的类型
// System.out.println(field.getType().getSimpleName());
// 获取属性的名字
// System.out.println(field.getName());
System.out.println(field.getType().getSimpleName()+":"+field.getName());
}
}
}
/*
* 通过反射机制,反编译一个类的属性
* */
public class ReflectTest06 {
public static void main(String[] args) throws Exception {
//创建这个是 为了拼接字符串
StringBuilder s=new StringBuilder();
Class studentClass=Class.forName("com.ma.bean.Student");
s.append(Modifier.toString(studentClass.getModifiers())+" class "+ studentClass.getSimpleName()+"{\n");
Field[]fields=studentClass.getDeclaredFields();
for (Field field:fields){
s.append("\t");
s.append(Modifier.toString(field.getModifiers()));
s.append(" ");
s.append(field.getType().getSimpleName());
s.append(" ");
s.append(field.getName());
s.append(";\n");
}
s.append("}");
System.out.println(s);
}
}
Object get(Object obj); // 返回指定对象上此 Field 表示的字段的值。
void set(Object obj, Object value); //将指定对象变量上此 Field 对象表示的字段设置为指定的新值。
Field getDeclaredField(String name); //根据属性名称获取Field
setAccessible(boolean flag); //打破封装,可访问私有属性
/*
* 怎么通过反射机制访问一个java对象的属性,
* 给属性赋值set
* 获取属性的值get
* */
public class ReflectTest07 {
public static void main(String[] args) throws Exception {
//不使用反射机制赋值
Student s=new Student();
s.no=1111; //三要素:1.Student对象s 2.no属性 3. 值 111
System.out.println(s.no);
//使用反射机制
Class studentClass =Class.forName("com.ma.bean.Student");
Object obj=studentClass.newInstance(); //student对象(低层调用无参数构造方法)
//获取no属性(根据属性名称获取Field)
Field noField= studentClass.getDeclaredField("no");
System.out.println(noField.getName()); //no
//给Student对象obj的no属性赋值
/*
*虽然使用了反射机制,但是三要素还是缺一不可
* 要素1:Student对象obj
* 要素2:no属性 noField
* 要素3:222值
* 注意:反射机制虽然让代码复杂了,但是为了一个 “灵活”,这也是值得的
* */
noField.set(obj,222); // 给obj对象的no属性赋值222
//获取属性的值
System.out.println(noField.get(obj)); //obj
//可以访问私有的属性嘛
Field nameField=studentClass.getDeclaredField("name");
//打破封装 (反射机制缺点:打破封装,会出现安全隐患)
//这样设置完之后在外部也是也可以访问private的
nameField.setAccessible(true);
//给name属性赋值
nameField.set(obj,"xiaoma");
//获取name属性值
System.out.println(nameField.get(obj));
}
}
/*
* 可变长参数: int... args
* 语法:类型... (注意一定是三个点)
*
* 1. 可变参数要求参数的个数是:0-N个。
* 2. 可变长度参数在参数列表中必须在最后一个位置上,而且可变长度参数只能有一个
* 3. 可变长度参数可以当一个数组来看待。
* */
public class ArgsTest {
public static void main(String[] args) {
m();
m(10);
m(10,22);
m3("abc","ab","de");
//传一个数组进去
String[]strs={"a","b","c"};
m3(strs);
m3(new String[]{"我","是","小","马"});
}
public static void m(int... args){
System.out.println("m方法执行了");
}
//报错
//public static void m2(String...args1,int...arg2){}
// 必须在最后,只能有1个
public static void m2(int a,String...args){}
public static void m3(String...args){
//args有length属性,说明args是一个数组
//可以将可变长度参数当做一个数组来看
for (int i=0;i<args.length;i++){
System.out.println(args[i]);
}
}
}
Method[] getDeclaredMethods(); //获取所有的Method,包括私有的,但不包括继承方法
int getModifiers();//获取修饰符列表
Class<?> getReturnType(); //获取返回值类型
String getName(); //获取方法名
Class<?>[] getParameterTypes(); //获取方法参数
/*
* 作为了解内容
* 反射Method
* */
public class ReflectTest08 {
public static void main(String[] args) throws Exception {
// 获取类
Class userServiceClass=Class.forName("com.ma.Service.UserService");
// 获取所有的Method(包括私有的)
Method[]methods=userServiceClass.getDeclaredMethods();
//System.out.println(methods.length); //2
//遍历Method
for (Method method:
methods ) {
// 获取修饰符列表
System.out.println(Modifier.toString(method.getModifiers()));
// 获取方法的返回值类型
System.out.println(method.getReturnType().getSimpleName());
//获取方法名
System.out.println(method.getName());
// 方法的参数(一个方法的参数可能会有多个。)
Class[]parameterTypes=method.getParameterTypes();
for (Class parameterType:
parameterTypes) {
System.out.println(parameterType.getSimpleName());
}
}
}
}
/*
* 反编译 Method
* */
public class ReflectTest09 {
public static void main(String[] args) throws Exception {
StringBuilder s=new StringBuilder();
Class userServiceClass = Class.forName("com.ma.Service.UserService");
s.append(Modifier.toString(userServiceClass.getModifiers())+"class"+userServiceClass.getSimpleName()+ "{\n");
Method[]methods= userServiceClass.getDeclaredMethods();
for (Method method:
methods) {
s.append("\t");
s.append(Modifier.toString(method.getModifiers()));
s.append(" ");
s.append(method.getReturnType().getSimpleName());
s.append(" ");
s.append(method.getName());
s.append("(");
//参数
Class[]paramerTypes=method.getParameterTypes();
for (Class paramerType:
paramerTypes) {
s.append(paramerType.getSimpleName());
s.append(",");
}
//删除指定下标位置上的字符
s.deleteCharAt(s.length()-1);
s.append("){}\n");
}
s.append("}");
System.out.println(s);
}
}
Method getDeclaredMethod(String name, Class<?>... parameterTypes); //获取method,返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法。
Object invoke(Object obj, Object... args); //调用方法 对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。
3.代码演示:
/*
* 用户业务类
* */
public class UserService {
/**
*
* @param name 用户名
* @param password 密码
* @return true:登录成功 false:登录失败
*/
public boolean login(String name,String password){
if ("admin".equals(name) && "123".equals(password)){
return true;
}
return false;
}
/*
* 退出系统方法
* */
public void logout(){
System.out.println("系统已经安全退出");
}
}
/*
*
* 重点:必须掌握,通过反射机制怎么调用一个对象的方法。
*
* 反射机制,让代码很具有通用性,可变化的内容都是写到配置文件当中的,将来
* 修改配置文件之后,创建的对象就不一样了,调用的方法也不同了,但是java代码不需要
* 做任何改动,这就是反射机制的魅力。
* */
public class ReflectTest10 {
public static void main(String[] args) throws Exception {
// 不使用反射机制,怎么调用方法
// 创建对象
UserService userService=new UserService();
boolean loginSuccess=userService.login("admin","123");
System.out.println(loginSuccess?"登录成功":"登录失败");
// 使用反射机制调用一个对象的方法该怎么做
Class userServiceClass = Class.forName("com.ma.Service.UserService");
// 创建对象
Object obj=userServiceClass.newInstance();
//获取method
//不能只通过方法名来确定方法,可能也会出现重载
//java中依靠方法名和参数列表区分一个方法
Method loginMethod=userServiceClass.getDeclaredMethod("login", String.class, String.class);
//调用方法
/*
* 要素1 : 对象userService
* 要素2 : login方法名
* 要素3 : 实参列表
* 要素4 : 返回值
* */
//invoke :调用 必须记住
/*
* 四要素:
* loginMethod方法,
* obj对象,
* "admin","123"实参
* retValue 返回值
*
*
* */
Object retValue=loginMethod.invoke(obj,"admin","123");
System.out.println(retValue);
}
}
Constructor<?>[] getConstructors(); //返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的所有公共构造方法。
int getModifiers(); //以整数形式返回此 Constructor 对象所表示构造方法的 Java 语言修饰符。
Class<?>[] getParameterTypes(); //按照声明顺序返回一组 Class 对象,这些对象表示此 Constructor 对象所表示构造方法的形参类型。
/*
* 反编译一个类的Constructor 构造方法
* */
public class ReflectTest11 {
public static void main(String[] args) throws Exception {
StringBuilder s=new StringBuilder();
Class vipClass=Class.forName("com.ma.bean.Vip");
s.append(Modifier.toString(vipClass.getModifiers())+" class "+vipClass.getSimpleName()+" {\n");
// 拼接构造方法
Constructor[]constructors=vipClass.getConstructors();
for (Constructor constructor:
constructors) {
s.append("\t");
s.append(Modifier.toString(constructor.getModifiers()));
s.append(" ");
s.append(vipClass.getSimpleName());
s.append("(");
//拼接参数
Class[]parameterTypes=constructor.getParameterTypes();
for (Class parameterType:
parameterTypes) {
s.append(parameterType.getSimpleName());
s.append(",");
}
// 删除最后下标位置上的字符
if(parameterTypes.length>0) {
s.deleteCharAt(s.length() - 1);
}
s.append("){}\n");
}
s.append("}");
System.out.println(s);
}
}
Constructor<T> getConstructor(Class<?>... parameterTypes); //返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。
/*
* 重要
*
* */
public class ReflectTest12 {
public static void main(String[] args) throws Exception {
//不使用反射机制如何创建对象
Vip v1=new Vip();
Vip v2=new Vip(110,"zhangsan","2001-10-11",true);
//使用反射机制怎么创建对象
Class c=Class.forName("com.ma.bean.Vip");
//调用无参数构造方法
Object obj=c.newInstance();
System.out.println(obj);
//调用有参数的构造方法。
//第一步:先获取到这个有参数的构造方法
Constructor con=c.getDeclaredConstructor(int.class,String.class,String.class,boolean.class);
//第二步:调用构造方法创建对象
Object newobj=con.newInstance(110,"xiaoma","1997-09-30",true);
System.out.println(newobj);
//调用无参数构造方法
Constructor nocon=c.getDeclaredConstructor();
Object noObj=nocon.newInstance();
System.out.println(noObj);
}
}
Class<? super T> getSuperclass(); //获取父类
Class<?>[] getInterfaces(); //获取类所实现的所有接口
/*
* 重点:给你一个类,怎么获取这个类的父类,已经实现了哪些接口
* */
public class ReflectTest13 {
public static void main(String[] args) throws Exception {
//String 举例
Class c=Class.forName("java.lang.String");
//获取String的父类
Class superClass=c.getSuperclass();
System.out.println(superClass.getName()); //java.lang.Object
//获取String类实现的所有接口(一个类可以实现多个接口)
Class[]interfaces=c.getInterfaces();
for (Class in:
interfaces ) {
System.out.println(in.getName());
}
}
}
/*
* 重点:给你一个类,怎么获取这个类的父类,已经实现了哪些接口
* */
public class ReflectTest13 {
public static void main(String[] args) throws Exception {
//String 举例
Class c=Class.forName("java.lang.String");
//获取String的父类
Class superClass=c.getSuperclass();
System.out.println(superClass.getName()); //java.lang.Object
//获取String类实现的所有接口(一个类可以实现多个接口)
Class[]interfaces=c.getInterfaces();
for (Class in:
interfaces ) {
System.out.println(in.getName());
}
}
}