目录
1.反射机制概述
反射机制有什么用
通过java语言中的反射机制可以操作字节码文件。
优点类似于黑客。(可以读和修改字节码文件。)
通过反射机制可以操作代码片段。(class文件。)
反射机制的相关类在哪个包下?
java.lang.reflect.*;
java.lang.Class; //代表字节码文件,代表一个类型,代表整个类
java.lang.reflect.Method; //代表字节码中的方法字节码,代表类中的方法
java.lang.reflect.Constructor; //代表字节码中的构造方法节码,代表类中的构造方法
java.lang.reflect.Field; //代表字节码中的属性字节码,代表类中的成员变量)(静态变量)
class:
public class User{
field:
int no;
constructor:
public User(){}
public User(int no){this.no=no;}
method:
public setNo(int no){this.no=no;}
public getNo(){return this.no;}
}
2.获取class的三种方式
要操作一个类的字节码,需要首先获取到这个类的字节码,怎么获取java.lang.class实例
三种方式
第一种:Class c=Class.forName(“完整类名带包名”);
第二种:Class c=对象.getClass();
第三种:Class c=任何类型.class;
2.1.Class.forName()方式
Class.forName();
- 1.静态方法
- 2.方法的参数是一个字符串
- 3.字符串需要一个完整的类名
- 4.完整类名必需带有包名,java.lang也不能省略
try {
Class c1=Class.forName("java.lang.String");//c1代表String.class文件,或者说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类型
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
2.2.obj.getClass()方式
java中任何一个对象都有一个方法:getClass();
String s="abc";
Class x=s.getClass(); //x代表String.class字节码文件,x代表String类型
System.out.println(c1==x); //true(==判断的是对象的内存地址)
Date time=new Date();
Class y=time.getClass();
//true(c2和y两个变量中保存的内存地址都是一样的,都指向方法区中的字节码文件)
System.out.println(c2==y);
注意:以上代码中c1和c2是上一种方式得到的变量
2.3…class方式
java语言中任何一种数据类型,包括基本数据类型,它都有.class属性
Class z=String.class; //z代表String类型
Class k=Date.class; //k代表Date类型
Class f=int.class; //f代表int类型
Class e=double.class; //e代表double类型
System.out.println(c1==z); //true
3.通过反射实例化对象
获取到Class能干什么
通过Class的newInstance()方法来实例化对象
注意:newInstance()方法内部实际上调用了无参构造方法,必须保证无参构造存在才可以
public class User {
public User(){
System.out.println("无参构造方法");
}
}
public class ReflectTest02 {
public static void main(String[] args) {
//这是不使用反射机制创建对象
User user=new User();
System.out.println(user);
//下面这段代码是以反射机制的方式创建对象
try {
//通过反射机制,获取Class,通过Class来实例化对象
Class c=Class.forName("reflect.User"); //c代表User对象
//newInstance()这个方法会调用User这个类的无参数构造方法,来完成对象的创建
//重点是:newInstance()调用的是无参构造,必须保证无参构造是存在的
Object obj=c.newInstance();
System.out.println(obj);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
4.通过读属性文件实例化对象
反射机制更灵活,灵活在哪里?
验证反射机制的灵活性
java代码写一遍,在不改变java源代码的基础之上,可以做到不同对象的实例化
非常之灵活,(符合OCP的原则,对扩展开放,对修改关闭)
后期要学习的是框架,而工作过程中,也都是使用高级框架,
这些高级框架底层实现原理,都采用了反射机制,所以反射机制是重要的,
学会了反射机制有利于你理解剖析底层的源代码
classInfo.properties的文件
className=cn.yujie.bean.User
public class ReflectTest01 {
public static void main(String[] args) throws Exception{
//这种方式代码就写死了,只能创建一个User类型的对象
//User user=new User();
//以下代码是灵活的,代码不需要改动,可以修改配置文件,配置文件修改之后,可以创建出不同的实例对象
try {
//通过IO流读取classInfo.properties文件
FileReader reader=new FileReader("chapter15/classInfo.properties");
//创建属性类对象Map
Properties pro=new Properties(); //keyvalue都是String
//加载
pro.load(reader);
//关闭流
reader.close();
//通过key获取value
String className=pro.getProperty("className");
//通过反射机制实例化对象
Class c=Class.forName(className);
Object obj=c.newInstance();
System.out.println(obj);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
5.只让静态代码快执行可以使用forName
public class ReflectTest02 {
public static void main(String[] args) {
try {
Class.forName("cn.yujie.reflect.MyClass");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
class MyClass{
//静态代码块在类加载时执行,并且只执行一次
static{
System.out.println("MyClass类的静态代码块执行了!");
}
}
记住:如果你只是希望一个类的静态代码快,其他代码一律不执行
你可以使用:
Class.forName(“完整类名”);
这个方法的执行会导致类加载,类加载时,静态代码块执行。
这种方式的路径的缺点是:移植性差,在IDEA中默认的当前路径是project的根。
这个代码假设离开了IDEA,换到了其他位置,可能当前路径就不是project的根了,这时这个路径就无效了。
FileReader reader=new FileReader(“chapter15/classInfo.properties”);
接下来说一种比较通用的一种路径,即使代码换位置了,这样编写仍然是通用的。
注意:使用以下通用方式的前提是:这个文件必须在类路径下
记住:凡是在src下的都是类路径下。
怎么获取一个文件的绝对路径,以下讲解的这种方式是通用的,但前提是:文件需要在类路径下,才能使用这种方式。
Thread.currentThread()当前线程对象
- getContextClassLoader()是线程对象的方法,可以获取到当前线程的类加载器对象
- getResource()这是类加载器对象的方法,当前线程的类加载器默认从类的根路径下加载资源【获取资源】
- getPath()获取绝对路径
//这种方式获取文件的绝对路径是通用的。
String path=Thread.currentThread().getContextClassLoader().getResource("classInfo2.properties").getPath();
System.out.println(path);
//获取db.properties文件的绝对路径(从类的根路径下作为起点开始)
String path2=Thread.currentThread().getContextClassLoader().getResource("cn/yujie/bean/classInfo2.properties").getPath();
System.out.println(path2);
6.以流的形式直接返回
public class IOProPertiesTest {
public static void main(String[] args) throws Exception{
try {
//获取一个文件的绝对路径
// String path=Thread.currentThread().getContextClassLoader().getResource("classInfo2.properties").getPath();
// FileReader reader=new FileReader(path);
//直接以流的形式返回。
InputStream reader=Thread.currentThread().getContextClassLoader().getResourceAsStream("classinfo2.properties");
Properties pro=new Properties();
pro.load(reader);
reader.close();
//通过key获取value
String className=pro.getProperty("className");
System.out.println(className);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
7.资源绑定器
java.util包下提供了一个资源绑定器,便于获取属性配置文件中的内容,
使用以下这种方式的时候,属性配置文件xxx.properties必须放到类路径下
public class ReSourceBundleTest {
public static void main(String[] args) {
//资源绑定器,只能绑定xxx.properties文件,并且这个文件必须在类路径下,文件扩展名必须是properties
//并且在写路径的时候,路径后面的扩展名不能写
ResourceBundle bundle =ResourceBundle.getBundle("classInfo2");
String className=bundle.getString("className");
System.out.println(className);
}
}
8.类加载器
3、关于JDK中自带的类加载器:(聊一聊,不需要掌握,知道当然最好!)
什么是类加载器?
- 专门负责加载类的命令/工具。
- ClassLoader
JDK中自带了3个类加载器 - 启动类加载器:rt.jar
- 扩展类加载器:ext/*.jar
- 应用类加载器:classpath
假设有这样一段代码:
String s = “abc”;
代码在开始执行之前,会将所需要类全部加载到JVM当中。通过类加载器加载,看到以上代码类加载器会找String.class文件,找到就加载,那么是怎么进行加载的呢?
首先通过“启动类加载器”加载。注意:启动类加载器专门加载:C:\Program Files\Java\jdk1.8.0_101\jre\lib\rt.jar
rt.jar中都是JDK最核心的类库。
如果通过“启动类加载器”加载不到的时候,会通过"扩展类加载器"加载。
注意:扩展类加载器专门加载:C:\Program Files\Java\jdk1.8.0_101\jre\lib\ext*.jar
如果“扩展类加载器”没有加载到,那么会通过“应用类加载器”加载。注意:应用类加载器专门加载:classpath中的类。
9.双亲委派机制
java中为了保证类加载的安全,使用了双亲委派机制。
优先从启动类加载器中加载,这个称为“父” “父”无法加载到,再从扩展类加载器中加载,这个称为“母”。双亲委派。如果都加载不到,才会考虑从应用类加载器中加载。直到加载到为止。
10.总结
1.什么是反射机制?反射机制有什么用?
反射机制:可以操作字节码文件
作用:可以让程序更加灵活。
2.反射机制相关的类在哪个包下?
java.lang.reflect.*;
3.反射机制相关的主要的类?
- java.lang.Class
- java.lang.reflect.Method;
- java.lang.reflect.Constructor;
- java.lang.reflect.Field;
4.在java中获取Class的三种方式?
第一种:
- Class c = Class.forName(“完整类名”);
第二种: - Class c = 对象.getClass();
第三种: - Class c = int.class;
- Class c = String.class;
5.获取了Class之后,可以调用无参数构造方法来实例化对象
//c代表的就是日期Date类型
Class c = Class.forName("java.util.Date");
//实例化一个Date日期类型的对象
Object obj = c.newInstance();
一定要注意:
newInstance()底层调用的是该类型的无参数构造方法。
如果没有这个无参数构造方法会出现"实例化"异常。
6.如果你只想让一个类的“静态代码块”执行的话,你可以怎么做?
- Class.forName(“该类的类名”);
- 这样类就加载,类加载的时候,静态代码块执行!!!!
- 在这里,对该方法的返回值不感兴趣,主要是为了使用“类加载”这个动作。
7.关于路径问题?
String path = Thread.currentThread().getContextClassLoader()
.getResource("写相对路径,但是这个相对路径从src出发开始找").getPath();
String path = Thread.currentThread().getContextClassLoader()
.getResource("abc").getPath(); //必须保证src下有abc文件。
String path = Thread.currentThread().getContextClassLoader()
.getResource("a/db").getPath(); //必须保证src下有a目录,a目录下有db文件。
String path = Thread.currentThread().getContextClassLoader()
.getResource("com/bjpowernode/test.properties").getPath();
//必须保证src下有com目录,com目录下有bjpowernode目录。
//bjpowernode目录下有test.properties文件。
这种方式是为了获取一个文件的绝对路径。(通用方式,不会受到环境移植的影响。)但是该文件要求放在类路径下,换句话说:也就是放到src下面。src下是类的根路径。
直接以流的形式返回:
InputStream in = Thread.currentThread().getContextClassLoader() .getResourceAsStream("com/bjpowernode/test.properties");
8.IO + Properties,怎么快速绑定属性资源文件?
//要求:第一这个文件必须在类路径下
//第二这个文件必须是以.properties结尾。
ResourceBundle bundle = ResourceBundle.getBundle("com/bjpowernode/test");
String value = bundle.getString(key);