1.创建class对象
Class类: 用来描述类或者接口的类型,描述类的类.
Class类的实例: 在JVM中的一份份字节码,Class实例表示在JVM中的类或者接口,枚举是一种特殊的类,注解是一种特殊的接口.
当程序第一次使用某一个java.util.Date类的时候,就会把该类的字节码对象加载进JVM,并创建出一个Class对象.
此时的Class对象就表示java.util.Date的字节码.
Class类可以表示N个类的字节码对象,问题,到底怎么区分Class类此时表示的那一个类的字节码呢?为了解决该问题,Class类的设计者提供了泛型.—>Class.
java.lang.String类的字节码类型: Class<java.lang.String>;
java.util.Date类的字节码类型: Class<java.util.Date>;
java.util.ArrayList类的字节码类型: Class<java.util.ArrayList>;
如何创建Class对象,如何来表示一个字节码对象?
public class ClassInstanceDemo {
public static void main(String[] args) throws Exception {
//创建Class对象和表示一个字节码对象
//需求:获取java.util.Date 类的字节码对象
//方式1: 使用 Class属性
Class<java.util.Date> clz1 = java.util.Date.class;
//方式2:同过对对象的getClass方法来获取,getClass是object类中的方法
//此时知道对象的类型
java.util.Date data = new java.util.Date();
Class<?> clz2 = data.getClass();
//方式3:通过Class类的静态方法forName(String className)
Class<?> clz3 = Class.forName("java.util.Date");
System.out.println(clz1);
System.out.println(clz2);
System.out.println(clz3);
}
}
其中在项目中大量用到第三种方式
2.获取类中的构造器
需求:通过反射来获取某一个类的构造器:
1):获取该类的字节码对象.
2):从该字节码对象中去找需要获取的构造器.
Class类获取构造器方法:
Constructor类:表示类中构造器的类型,Constructor的实例就是某一个类中的某一个构造器
public Constructor<?>[] getConstructors():该方法只能获取当前Class所表示类的public修饰的构造器
public Constructor<?>[] getDeclaredConstructors():获取当前Class所表示类的所有的构造器,和访问权限无关
//使用反射调用构造器创建对象
import java.lang.reflect.Constructor;
class Person {
public Person() {
System.out.println("无参构造器");
}
public Person(String name) {
System.out.println("构造器" + name);
}
private Person(String name, int age) {
System.out.println("构造器" + name + age);
}
}
public class CreateObjectDemo {
public static void main(String[] args) throws Exception {
Class<Person> clz = Person.class;
// 调用public Person()
Constructor<Person> con = clz.getConstructor();
// 调用构造器的newInstance方法来创建对象,并传入实参
con.newInstance();
System.out.println("=============");
// 调用public Person(String name)
con= clz.getConstructor(String.class);
con.newInstance("linwei");
System.out.println("=================");
//调用private Person(String name, int age)
con = clz.getDeclaredConstructor(String.class,int.class);
//设置当前构造器可以访问
con.setAccessible(true);
con.newInstance("linwei",22);
}
}
3.使用反射调用获取的构造器
import java.lang.reflect.Constructor;
class User {
public User() {
}
public User(String name) {
}
private User(String name, int age) {
}
}
public class GetConstructorInstanceDemo {
public static void main(String[] args) throws Exception {
getAll();
getOne();
}
private static void getAll() {
// 1:获取构造器所在类的字节码对象
Class<User> clz = User.class;
// 2 : 获取clz对象中的所有构造器
// public Constructor<?>[]getConstructors(),
//该方法只能获取当前Class所表示的类 public 修饰的构造器
Constructor<?>[] cs = clz.getConstructors(); // 方法后面加S的表示获取所有的,不加S表示获取单个
System.out.println(cs.length);
for (Constructor<?> c : cs) {
System.out.println(c);
}
System.out.println("================");
// public Constructor<?>[] getDeclaredConstructors(),
// 该方法只能获取当前Class所表示的类的所有构造器.和访问权限无关
cs = clz.getDeclaredConstructors();
System.out.println(cs.length);
for (Constructor<?> c : cs) {
System.out.println(c);
}
System.out.println("================");
}
//获取指定的一个构造器的
private static void getOne() throws Exception {
//1.获取构造器所在类的字节码对象
Class<User> clz = User.class;
//2. 获取clz对象中所有的构造器
//需求1 .获取public User()
Constructor con = clz.getConstructor();
System.out.println(con);
//2.获取public User (String name)
con = clz.getConstructor(String.class);
System.out.println(con);
//需求3. 获取private User(String name ,int age)
con =clz.getDeclaredConstructor(String.class,int.class);
System.out.println(con);
}
}
构造器最大的作用:创建对象.
为什么使用反射创建对象,为什么不直接来new呢?
在框架中,提供给我们的都是字符串.
使用反射创建对象:
步骤:
1);找到构造器所在类的字节码对象.
2):获取构造器对象.
3):使用反射,创建对象
Constructor类:表示类中构造器的类型,Constructor的实例就是某一个类中的某一个构造器
常用方法:
public T newInstance(Object… initargs):如调用带参数的构造器,只能使用该方式.
参数:initargs:表示调用构造器的实际参数
返回:返回创建的实例,T表示Class所表示类的类型
如果:一个类中的构造器是外界可以直接访问,同时没有参数.,那么可以直接使用Class类中的newInstance方法创建对象.
public Object newInstance():相当于new 类名();
调用私有的构造器:
4.获取类中的方法
使用反射获取类中的方法:
1):获取方法所在类的字节码对象.
2):获取方法.
Class类中常用方法:
public Method[] getMethods():获取包括自身和继承过来的所有的public方法
public Method[] getDeclaredMethods():获取自身类中所有的方法(不包括继承的,和访问权限无关)
public Method getMethod(String methodName,
Class<?>… parameterTypes):表示调用指定的一个公共的方法(包括继承的)
参数:
methodName: 表示被调用方法的名字
parameterTypes:表示被调用方法的参数的Class类型如String.class
public Method getDeclaredMethod(String name,
Class<?>… parameterTypes):表示调用指定的一个本类中的方法(不包括继承的)
//获取类中的方法
import java.lang.reflect.Method;
class User {
public void work() {
}
public static void work(String name) {
}
private String say(String name, int age) {
return name + "," + age;
}
}
public class MethodDemo {
public static void main(String[] args) throws Exception {
getAll();
getOne();
}
// 获取类中指定的一个方法
private static void getOne() throws Exception {
Class clz = User.class; // 获取该类的字节码文件
Method m = clz.getMethod("work"); // 方法名+ 参数
System.out.println(m);
m = clz.getMethod("work", String.class);
System.out.println(m);
m = clz.getDeclaredMethod("say", String.class, int.class);
System.out.println(m);
}
private static void getAll() {
// 获取指定类的字节码文件
Class clz = User.class;
// 需求类中的所有方法
Method[] ms = clz.getMethods();
System.out.println(ms.length); // 11 包括自身和继承过来的所有方法
for (Method m : ms) {
System.out.println(m);
}
System.out.println("========");
Method[] ms2 = clz.getDeclaredMethods();// 获取自身类中的所有方法,不包括继承 和访问权限无关
System.out.println(ms2.length);// 3
for (Method m : ms2) {
System.out.println(m);
}
System.out.println("=====================");
}
}
5.使用反射调用方法
使用反射调用方法:
1):获取方法所在类的字节码对象.
2):获取方法对象.
3):使用反射调用方法.
如何使用反射调用一个方法:
在Method类中有方法:
public Object invoke(Object obj,Object… args):表示调用当前Method所表示的方法
参数:
obj: 表示被调用方法底层所属对象
Method m = clz.getMethod(“sayHi”,String.class);
args:表示调用方法是传递的实际参数
返回:
底层方法的返回结果
调用私有方法:
在调用私有方法之前:应该设置该方法为可访问的
又因为Method是AccessibleObject子类,所以Method中具有该方法.
sayGoodByeMethod.setAccessible(true);
import java.lang.reflect.Method;
import java.util.Date;
//使用反射调用方法
class Person {
public void work() {
System.out.println("Person.work()"); // syst 快捷键
}
public static void work(String name) {
System.out.println("Person.work()" + name);
}
private String say(String name, int age) {
System.out.println("Person.say()" + name + "," + age);
return name + "," + age;
}
}
public class MethodInvokeDemo {
public static void main(String[] args) throws Exception {
// 获取指定类的字节码文件
Class clz = Person.class;
Method m = clz.getMethod("work"); // new Person().work("xxx") 正常调用第二个方法
// System.out.println(m);
Object ret = m.invoke(clz.newInstance());
// System.out.println(ret); //null 表示空返回 方法使用void修饰
// 调用public static void work(String name) 调用静态方法
m = clz.getMethod("work", String.class);// 得到该方法
ret = m.invoke(null, "linwei"); // 没有返回 //静态方法的第一个参数设置为
// null(静态方法不需要对象,任何字段都不会报错,根本不会用到)
// invoke的第一个参数设置为null,
// 可以调用静态方法
// 调用private String say(String name, int age)
m = clz.getDeclaredMethod("say", String.class, int.class);
// 设置可访问的
m.setAccessible(true);
// 调用方法
ret = m.invoke(clz.newInstance(), "linwei", 22);
System.out.println(ret);
System.out.println("===========================");
Object obj = new Date();
Class clz2 = obj.getClass();
// System.out.println(clz2);
Method method = clz2.getMethod("toLocaleString");
Object ret2 = method.invoke(obj);
System.out.println(ret2);
}
}
6.使用反射调用静态方法和数组参数
使用反射调用静态方法:
静态方法不属于任何对象,静态方法属于类本身.
此时把invoke方法的第一个参数设置为null即可.
使用反射调用数组参数(可变参数):
王道:调用方法的时候把实际参数统统作为Object数组的元素即可.
Method对象.invoke(方法底层所属对象,new Object[]{ 所有实参 });
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
//使用反射调用静态方法和数组参数 (可变参数) Method对象.invoke(方法底层所属对象,new Object[]{ 所有实参 });
// 静态方法不属于任何对象,静态方法属于类本身.
// 此时把invoke方法的第一个参数设置为null即可
class Employee {
public void work1(int... arr) {
System.out.println("work1" + Arrays.toString(arr));
}
public static String work2(String... arr) {
System.out.println("work2" + Arrays.toString(arr));
return "a";
}
public static <T> List<T> asList(T...a){
return null ;
}
}
public class MethodInvokeDemo2 {
public static void main(String[] args) throws Exception {
Class clz = Employee.class;
//泛型方法的获得 (T只是占位符,底层还是Object)
Method method2= clz.getMethod("asList", Object[].class);
System.out.println(method2);
// 数组的元素是基本类型
Method m = clz.getMethod("work1", int[].class);
Object ret = m.invoke(clz.newInstance(), new int[] { 1, 2, 2 });
Object ret1 = m.invoke(clz.newInstance(), new Object[] { new int[] { 1,
2, 3, 4, 5 } });
System.out.println(ret); // 返回null
// 数组的元素类型是引用类型(引用类型的数组会自动解包)
m = clz.getMethod("work2", String[].class);
Object ret2 = m.invoke(null, new Object[] { new String[] { "a", "b",
"c" } });
System.out.println(ret2);//返回为2
}
}
7.操作反射其他API
Class类中:
int getModifiers():获得修饰符
String getName():返回类的全限定名
Package getPackage():获得该类的包
String getSimpleName():获得类的简单名字
Class getSuperclass():获得类的父类
boolean isArray():判断该Class实例是否是数组
boolean isEnum() :判断该Class实例是否是枚举
Constructor,Method,Filed的信息:
去查阅相应类的API即可…
import java.awt.Dialog.ModalityType;
import java.lang.reflect.Modifier;
import javax.naming.directory.ModificationItem;
enum Person{
MAN,WOMAN; //枚举没有构造器
}
public class OtherApiDemo {
public static void main(String[] args) {
Class clz = OtherApiDemo.class;
//获取类的修饰符
int mod = clz.getModifiers(); //返回的是一个为int的数
System.out.println(mod);
String m = Modifier.toString(mod);
System.out.println(m);
//获取类的名称
System.out.println(clz.getName());//获取全限定名称 com._520it._04_other.OtherApiDemo
System.out.println(clz.getSimpleName()); //获取简单名称 OtherApiDemo
System.out.println(clz.getPackage()); //获取包名 com._520it._04_other
System.out.println(clz.getSuperclass());//获取类的父类
Class clz2= Person.class;
System.out.println(clz2.isArray());//判断class是否数组
System.out.println(clz2.isEnum());//判断class是否枚举
}
}
8.加载资源文件路径
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
public class LoadResourceDemo {
public static void main(String[] args) throws Exception {
test1();
test2(); //使用较多
test3();
}
//方式3:使用相对路径-相对于当前加载资源文件的字节码路径
//bin/com/_520it/_06_load_resource路径去寻找.
private static void test3() throws Exception {
Properties p = new Properties();
InputStream inStream =LoadResourceDemo.class.getResourceAsStream("db.properties");
p.load(inStream);//加载
System.out.println(p);
}
//方式2:使用相对路径-相对于classpath的根路径(字节码输出目录).
//ClassLoader(类加载器),类加载器默认就是从classpath根路径去寻找文件的.
private static void test2() throws Exception {
Properties p = new Properties();
//两种获取类加载器的方法
//ClassLoader loader = LoadResourceDemo.class.getClassLoader();
ClassLoader loader = Thread.currentThread().getContextClassLoader();
InputStream inStream =loader.getResourceAsStream("db.properties");
p.load(inStream);//加载
System.out.println(p);
}
//方式1:使用绝对路径的方式加载. 该方式不可行.
private static void test1() throws Exception {
Properties p = new Properties();
InputStream inStream = new FileInputStream("C:/Users/Lin/Desktop/Project/08反射机制/resource/db.properties");
p.load(inStream);//加载
System.out.println(p);
}
}