内容大纲
最重要的是Method和Field
反射机制(比较简单,因为只要会查帮助文档,就可以了。)
1.1、什么是反射机制?反射机制有什么用?
反射机制:可以操作字节码文件
作用:可以让程序更加灵活。
1.2、反射机制相关的类在哪个包下?
java.lang.reflect.*;
1.3、反射机制相关的主要的类?
java.lang.Class
java.lang.reflect.Method;
java.lang.reflect.Constructor;
java.lang.reflect.Field;
1.4、在java中获取Class的三种方式?
第一种:
Class c = Class.forName("完整类名");
第二种:
Class c = 对象.getClass();
第三种:
Class c = int.class;
Class c = String.class;
1.5、获取了Class之后,可以调用无参数构造方法来实例化对象
//c代表的就是日期Date类型
Class c = Class.forName("java.util.Date");
//实例化一个Date日期类型的对象
Object obj = c.newInstance();
一定要注意:
newInstance()底层调用的是该类型的无参数构造方法。
如果没有这个无参数构造方法会出现"实例化"异常。
1.6、如果你只想让一个类的“静态代码块”执行的话,你可以怎么做?
Class.forName("该类的类名");
这样类就加载,类加载的时候,静态代码块执行!!!!
在这里,对该方法的返回值不感兴趣,主要是为了使用“类加载”这个动作。
1.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");
1.8、IO + Properties,怎么快速绑定属性资源文件?
//要求:第一这个文件必须在类路径下
//第二这个文件必须是以.properties结尾。
ResourceBundle bundle = ResourceBundle.getBundle("com/bjpowernode/test");
String value = bundle.getString(key);
如何获取到Class实例
要操作一个类的字节码,需要首先获取到这个类的字节码,怎么获取java.lang.Class实例?
三种方式
-
第一种:Class c = Class.forName("完整类名带包名");
-
第二种:Class c = 引用.getClass();
public class ReflectTest01 {
public static void main(String[] args) {
/*
* Class.forName()
* 1、静态方法
* 2、字符串需要的是一个完整类名。
* 3、完整类名必须带有包名。java.lang包也不能省略。
* */
Class c1 = null;
try {
c1 = Class.forName("java.lang.String");//c1代表String.class文件,或者说代表String类型
Class 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 = "abc";
Class x = s.getClass();//x代表String.class文件,或者说代表String类型
System.out.println(c1 == x);//true 地址相同
//第三种方式,java语言中任何一种类型,包括基本数据类型,它都有.class属性。
Class z = String.class;//z代表String类型
System.out.println(x == z);//true
}
}
获取到实例能干什么
反射机制更具有灵活性
public class ReflectTest02 {
public static void main(String[] args) {
//通过反射机制,获取Class,通过Class来实例化对象
try {
Class c = Class.forName("com.bjpowernode.java.bean.User");
//newInstance()这个方法会调用User这个类的无参数构造方法,完成对象的创建。
//重点是:newInstance()调用的是无参构造,必须保证无参构造是存在的!
//如果User里写了有参构造,而不手动写出无参构造,无参构造就失效了
Object obj = c.newInstance();
System.out.println(obj);
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
验证反射机制的灵活性
创建com.bjpowernode.java.reflect.ReflectTest03
import java.io.FileReader;
import java.util.Properties;
public class ReflectTest03 {
public static void main(String[] args) throws Exception {
//通过IO流读取classinfo.properties文件
FileReader reader = new FileReader("chapter15/classinfo.properties");
//创建属性类对象Map
Properties pro = new Properties();//key value都是String
//加载
pro.load(reader);
//关闭流
reader.close();
//通过key获取value
String className = pro.getProperty("className");
System.out.println(className);
//通过反射机制实例化对象
Class c = Class.forName(className);
Object obj = c.newInstance();
System.out.println(obj);
}
}
com.bjpowernode.java.bean.User
public class User {
public User() {
System.out.println("无参数构造方法!");
}
//这里注意如果写了有参构造,而不手动写出无参构造,无参构造就失效了
}
在同一工程下创建classinfo.properties配置文件
className=com.bjpowernode.java.bean.User
此时输出结果为
com.bjpowernode.java.bean.User IO流结果
无参数构造方法!
com.bjpowernode.java.bean.User@5674cd4d 反射机制
这时我们只需要更改配置文件中的
如改为className=java.util.Date
从而改变了反射机制创建的对象
结果变为:
java.util.Date
Sat May 02 09:52:35 CST 2020
结论:java代码写一遍,在不改变java源代码的基础之上,可以做到不同对象的实例化。非常之灵活。(符合OCP开闭原则:对扩展开放,对修改关闭。可以修改配置文件,创建除不同的实例对象。
1、可以访问class文件
2、反射机制使程序更加灵活
后期学习的是高级框架,而工作过程中,也都是使用高级框架,
- 包括:ssh ssm
- Spring SpringMVC MyBatis
- Spring Structs Hibernate
- 这些高级框架底层实现原理:都采用了反射机制。所以反射机制还是重要的。
- 有利于剖析框架底层的源代码。
Class.forName()发生了什么?
记住,重点:
-
如果你只是希望一个类的静态代码块执行,其它代码一律不执行,
-
你可以使用:
-
Class.forName("完整类名");
-
这个方法的执行会导致类加载,类加载时,静态代码块执行。
示例:
public class ReflectTest04 {
public static void main(String[] args) {
try {
Class.forName("com.bjpowernode.java.reflect.MyClass");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
class MyClass{
//静态代码块在类加载时执行,并且只执行一次。
static {
System.out.println("MyClass类的静态代码块执行了!");
}
}
结果:MyClass类的静态代码块执行了!
文件路径的问题
通用路径:注意使用以下通用方式的前提是,这个文件必须在类路径下。
即src下的都是类路径。src是类的根路径。
public class AboutPath {
public static void main(String[] args) throws Exception{
/*这种方式的路径缺点是:移植性差,默认是project的根路径,离开IDEA就不是了。
* FileReader reader = new FileReader("chapter15/classinfo.properties");
* */
//通用路径:注意使用以下通用方式的前提是,这个文件必须在类路径下。
//即src下的都是类路径。src是类的根路径。
/*Thread.currentThread() 当前线程对象
* getContextClassLoader() 是线程对象的方法,可以获取到当前线程的类加载器对象
* getResource() 【获取资源】这是类加载器对象的方法,当前线程的类加载器默认从类的根路径下加载资源。
* */
//采用以上的代码可以拿到一个文件的绝对路径
///D:/learn/Java%20IDEA/out/production/chapter15/classinfo2.properties
String path = Thread.currentThread().getContextClassLoader()
.getResource("classinfo2.properties").getPath();
System.out.println(path);
}
}
以流的形式返回
import java.io.FileReader;
import java.io.InputStream;
import java.util.Properties;
public class IoPropertiesTest {
public static void main(String[] args)throws Exception{
//可以获取一个文件的绝对路径了 通用
/*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);
}
}
资源绑定器
好用,但是有使用限制:
1、java.util包下提供了一个资源绑定器,便于获取属性配置文件中的内容。使用以下这种方式的时候,属性配置文件xxx.properties必须放到src类路径下。
2、只能绑定xxx.properties文件,扩展名必须是properties
3、在写路径的时候,路径后面的扩展名不能写。
public class ResourceBundleTest {
public static void main(String[] args) {
//只能绑定xxx.properties文件,扩展名必须是properties
//在写路径的时候,路径后面的扩展名不能写。
//ResourceBundle bundel = ResourceBundle.getBundle("classinfo2");
//如果在别的路径下
ResourceBundle bundel = ResourceBundle.getBundle("com/bjpowernode/java/bean/db");
String className = bundel.getString("className");
System.out.println(className);
}
}
获取field(了解)
//反射属性Field
public class Student {
//Field翻译为字段,其实就是属性/成员
//4个
private String name;//Field对象
protected int age;
boolean sex;
public int no;
}
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
public class ReflectTest05 {
public static void main(String[] args) throws ClassNotFoundException {
//获取类中所有的public修饰的Field
Class studentClass = Class.forName("com.bjpowernode.java.bean.Student");
String className = studentClass.getName();
System.out.println("完整类名" + className);
String simpleName = studentClass.getSimpleName();
System.out.println(simpleName);
//获取类中所有的Field
Field[] fields = studentClass.getFields();
System.out.println(fields.length);//测试数组中只有一个元素
//取出这个Field
Field f = fields[0];
//取出这个Field它的名字
String fieldName = f.getName();
System.out.println(fieldName);
//获取所有的Field
Field[] fs = studentClass.getDeclaredFields();
System.out.println(fs.length);//4
System.out.println("===============");
//遍历
for(Field f1 : fs){
//获取属性的修饰符列表 有可能有多个
int i = f1.getModifiers();//返回的修饰符是一个数字,每个数字是修饰符的代号!!!
System.out.println(i);
//可以将这个“代号”数字转换成“字符串”吗?
String modifierString = Modifier.toString(i);
System.out.println(modifierString);
//获取属性的类型
Class fieldType = f1.getType();
String fName =fieldType.getName();//getSimpleName()获取简单类型
System.out.println(fName);
//获取属性的名字
System.out.println(f1.getName());
}
}
}
输出结果
完整类名com.bjpowernode.java.bean.Student
Student
1
no
===============
4
2
private
java.lang.String
name
4
protected
int
age
0
boolean
sex
1
public
int
no
反编译(非重点)
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
//通过反射机制,反编译一个类的属性Field
public class ReflectTest06 {
public static void main(String[] args) throws ClassNotFoundException {
//创建这个是为了拼接字符串
StringBuffer s = new StringBuffer();
//反编译拿到的Class文件
Class studentClass = Class.forName("java.lang.String");
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);
}
}
通过反射机制访问对象属性(重点)
注意:反射机制代码复杂了,但是为了灵活。
不会自己去写反射机制代码,但有助于理解框架
import java.lang.reflect.Field;
public class ReflectTest07 {
public static void main(String[] args) throws Exception {
/*
* 传统做法:访问对象的属性
* Student s = new Student();
* s.no = 1111;
* */
//使用反射机制,怎么去访问一个对象的属性。(set get)
Class studentClass = Class.forName("com.bjpowernode.java.bean.Student");
Object obj = studentClass.newInstance();//obj就是Student对象。(底层调用无参数构造方法)
//获取no属性(根据属性的名称来获取Field)
Field noFiled = studentClass.getDeclaredField("no");
//给obj对象(Student对象)的no属性赋值
/*
* 虽然使用了反射机制,但是三要素还是缺一不可;
* 1、对象obj
* 2、no属性
* 3、22222值
* */
noFiled.set(obj, 22222);//给obj对象的no属性赋值2222
//读取属性的值
//两个要素:获取obj对象的no属性的值。
System.out.println(noFiled.get(obj));
}
}
可变长度参数
可变长度参数
-
int...args这就是可变长度
-
1、参数:0~N个
-
2、有且只能在最后位置,一个
-
3、可以当作一个数组来看待
-
语法是:类型...(注意:一定是3个点。)
public class ArgsTest {
public static void main(String[] args) {
m();
m(10);
m(10,20);
m3("ab","cd","ef");
}
public static void m(int...args){
System.out.println("m方法执行了!");
}
public static void m2(String a,int...args){
System.out.println("m2方法执行了!");
}
public static void m3(String...args){
for(int i=0;i<args.length;i++) {
System.out.println(args[i]);
}
}
}
反射机制调用方法
/*
- 用户业务类
- */
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("系统已经安全退出!");
}
}
反射机制调用UserService方法
import com.bjpowernode.java.service.UserService;
import java.lang.reflect.Method;
public class ReflectTest10 {
public static void main(String[] args) throws Exception {
//不是用反射机制,如何调用方法
UserService user = new UserService();
boolean loginSuccess = user.login("admin","123");
System.out.println(loginSuccess);
//使用反射机制来调用一个对象的方法该怎么做?
Class userServiceClass = Class.forName("com.bjpowernode.java.service.UserService");
//创建对象
Object obj = userServiceClass.newInstance();
//获取Method 限制方法名和参数
Method loginMethod = userServiceClass.getDeclaredMethod("login", String.class, String.class);
//调用方法
//四要素:obj对象、loginMethod方法、"admin","123"参数、retValue返回值
Object retValue = loginMethod.invoke(obj,"admin","123");
System.out.println(retValue);
}
}
给你一个类,怎么获取这个类的父类,已经实现了哪些接口
public class ReflectTest13 {
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());//Object
//获取其实现的多个接口
Class[] interfaces = stringClass.getInterfaces();
for(Class in : interfaces){
System.out.println(in.getName());
}
}
}