1.获取类中的构造器
需求:通过反射来获取一个类中的构造器
1)获取该类的字节码对象
2)从该字节码对象中寻找需要获取的构造器
class获取类的构造器的方法
constructor类:表示类中得构造器得类型,constructor得实例是某一个类中得某一个构造器
参数parameterTypes:构造参数的具体类型
如:public User(String name)
Constructor c = clz.getConstructor(String.class)
//获取指定的构造器
private static void getOne() throws Exception, SecurityException {
//1.获取类的字节码对象
Class<?> clz = user.class;
//2.获取clz中指定的构造器
//需求1:获取public user(){}
Constructor<?> cs = clz.getConstructor();
System.out.println(cs);
//需求2:获取public user(String name){}
Constructor<?> cs1= clz.getConstructor(String.class);
System.out.println(cs1);
//需求3:获取public user(String name , int age){}
Constructor<?> cs2 = clz.getConstructor(String.class, int.class);
System.out.println(cs2);
}
2.使用反射获取构造器,创建对象
为什么使用反射创建对象?
因为在框架中,提供给我们的都是字符串
使用反射创建对象:
步骤:
(1)找到构造器所在类的字节码对象
(2)获取构造器对象
(3)使用反射创建对象
Constructor<?>表示构造器的类,constructor的实例表示一个类中的一个构造器
常用方法:
public T newInstance(Object... initargs) :如果调用带参数的构造器,只能使用该方法
参数initargs:表示调用构造器使用的实际参数
返回值T:返回创建的实例,T表示实际的class类型
如果一个类中的构造器可以直接访问,同时没有参数,那么可以直接使用class中的newInstance方法创建对象
class Person {
public Person() {
System.out.println("无参数构造器");
}
public Person(String name) {
System.out.println("name构造器" + name);
}
private Person(String name, int age) {
System.out.println("name,age构造器" + name + age);
}
}
public static void main(String[] args) throws Exception {
// 获取不带参数的公共构造器,创建对象
Class<Person> clz1 = Person.class;
// 获取类中的构造器
Constructor<Person> cs1 = clz1.getConstructor();
// 使用newinstance方法来创建对象
Person p = cs1.newInstance();
//无参,公共构造器,可以直接使用class类名.newinstance()
clz1.newInstance();
System.out.println(p);
System.out.println("------------------------------");
//获取带参数的公共构造器,并传入参数创建对象
Class<Person> clz2= (Class<Person>) Class.forName("demo.Person");
Constructor<Person> cs2 = clz2.getConstructor(String.class);
Person person2 = cs2.newInstance("jimmy");
System.out.println(person2);
System.out.println("------------------------------");
//获取带参数的私有构造器,并传入参数
Constructor<Person> cs3 = clz2.getDeclaredConstructor(String.class,int.class);
//设置当前构造器可以访问(反射暴力破坏了封装和访问权限)
cs3.setAccessible(true);
/*没有这里的设置的话会有报错:
Exception in thread "main" java.lang.IllegalAccessException: Class demo.CreateObjectDemo can not access a member of class demo.Person with modifiers "private"
报错:不允许访问private修饰的构造器*/
cs3.newInstance("allen",20);
}
同理,构造器可以设置可访问,那么字段和方法都可以这样设置
3.使用反射获取类中的方法
步骤:
(1)获取方法所在类的字节码对象
(2)获取方法
----------------------------------------------
class类中常用方法:
public Method[ ] getMethods:
获取包括自身类和所有继承过来所有
public
方法
public Method[ ] getDeclaredMethods
;获取自身类中的所有方法(不包括继承的和
访问权限
无关)
getMethod(String name, 类<?>... parameterTypes):表示调用指定的一个公共方法(不包括继承的)
name:指定的方法名
parameterTypes:表示被调用方法的参数列表
返回:
the
Method object that matches the specified
name and
parameterTypes
getDeclaredMethod(String name, 类<?>... parameterTypes) :
参数:
name- 指定的方法名
parameterTypes- 表示被调用方法的参数列表
返回:
the
Method object for the method of this class matching the specified name and parameters
class User{
public static void dowork(){
System.out.println("dowork..");
}
public static void dowork(String a){
System.out.println("dowork "+ a);
}
private String sayHello(String name){
return "hello,"+name;
}
}
//获取指定的方法
public static void getone() throws Exception {
//需求获取dowork方法
Class<?> clz= Class.forName("_method.User");
Method method = clz.getMethod("dowork", String.class);
System.out.println(method);
//需求获取sayHello方法
method=clz.getDeclaredMethod("sayHello", String.class);
System.out.println(method);
}
//获取User中的全部方法
public static void getAll() throws Exception {
Class<?> cs1 = Class.forName("_method.User");
Method[] methods= cs1.getMethods();
for(Method m : methods){
System.out.println(m);
}
4.使用反射调用方法
1.调用对象方法
调用方法即是执行方法
如何调用一个方法:
在Method类中有方法:
obj
- 被调用方法底层所属对象
args
- 表示调用方法所传递的实际参数
返回:
底层方法的返回结果
调用私有方法:
调用前,应该设置其为可访问的
Method是Accessiable的子类,故Method有该方法
Method.setAccessiable(true);
// 调用dowork()方法
Class<?> clz = Class.forName("_method.Person");
Method m = clz.getMethod("dowork");
System.out.println(m);
m.invoke(clz.newInstance());
System.out.println("-----------------------");
// 调用dowork(String a)方法
Method m2 = clz.getMethod("dowork",String.class);
System.out.println(m2);
m2.invoke(clz.newInstance(),"Lily");
System.out.println("-----------------------");
// 调用SayHello(String a)方法
Method m3 = clz.getDeclaredMethod("sayHello",String.class);
System.out.println(m3);
//设置m3方法为可访问
m3.setAccessible(true);
Object ret= m3.invoke(clz.newInstance(),"Lily");
System.out.println(ret);
2.调用类的静态方法
静态方法不属于任何对象,只属于类本身
此时把invoke方法的第一个参数设置为null即可
3.调用数组参数对象
王道:调用方法的时候把实际参数放到new Object{}数组的元素即可
Method.invoke(方法所属对象,new Object{所有实参})
class Employee {
public static void doWork1(int[] arr) {
System.out.println("dowork_:" + Arrays.toString(arr));
}
public static void doWork2(String... arr) {
System.out.println("dowork_:" + Arrays.toString(arr));
}
}
// 使用反射调用数组参数
public class _MethodInvokeDemo2 {
public static void main(String[] args) throws Exception {
//数组的类型是基本数据类型
Class<?> clz1 = Class.forName("_method.Employee");
Method method = clz1.getMethod("doWork1", int[].class);
method.invoke(null,new int[]{1,2,3,4,5});
//数组的类型是基本引用类型
Method method2= clz1.getMethod("doWork2", String[].class);
//method2.invoke(null,new String[]{"a","b","c","d"}); ERROR 引用数据类型调用的时候被自动解包
method2.invoke(null,new Object[]{new String[]{"a","b","c","d"}});
}
}
5.操作反射其他的API
Class类中:
int getModifies():获取修饰符
String getName():获取全限定名
Package getPackage():获取该类的包名
Class getSuperclass()获取类的父类
boolean isArray():判断是否为数组
boolean isEnum():判断是否为枚举
注意: 枚举类型不能通过反射获取类型和字段
6.加载资源文件路径:
db.properties
注意:加载properties方法只能用Properties类的load方法
使用相对路径-:
相对于
classpath的根路径(
字节码输出目录 ):
通过类加载器得到classpath的根路径.
resources是javaset中的 source folder, 这里面的文件会编译到
classpath的根路径
public static void main(String[] args) throws Exception {
Properties ps = new Properties();
//方式1:通过线程类的获取加载器的方法,得到类加载器
ClassLoader classld = Thread.currentThread().getContextClassLoader();
//方式2:通过类名.class.获取加载器的方式
// ClassLoader classld2 = ClassDemo.class.getClassLoader();
InputStream ins =classld.getResourceAsStream("db.properties");
ps.load(ins);
System.out.println(ps);
}