JAVA放射机制
反射是java的动态机制,它允许我们在程序[运行期间]再来确定要实例化的对象,调用的方法或操作的属性,该机制可以大大的提高代码的灵活度和可扩展性,但是也随之带来了较低的运行效率和更多的系统开销,因此程序不应当过度利用放射机制,
类对象
放射机制第一步,获取该操作类的类对象
Class类的实例,该实例用于表示JVM中加载的一个类
当JVM加载一个类时就会实例化一个Class对象与之对象,并且每个被加载的类有且只有一个Class的实例与之绑定,通过该Class实例我们可以了解到其表示的类的一切信息(类名,多少个方法,多少个构造器,多少个属性等)
获取类对象的方式
1:类名.class
Class cls=String.class;
Class cls=int.class
注:基本类型获取对象只有上述方式
2.Class.forName(String className)
基于类的完全限定名(包名,类名)加载一个类
Class cls=class.forName("java.lang.string");
3.ClassLoader类加载器形式
public class ReflectDemo1 {
public static void main(String[] args) throws ClassNotFoundException {
//Class cls=String.class;//获取String类对象
//Class cls=Class.forName("java.long.String");
Scanner scan=new Scanner(System.in);
System.out.println("请输入一个类名");//这里输入的类名要代类的全称,就是要代包名
String className=scan.nextLine();
Class cls=Class.forName(className);
String name=cls.getName();
System.out.println(name); //获取的是我们在控制台输入的名字
//仅仅获取类的名字(不含包名)
name=cls.getSimpleName();
System.out.println("类名:"+name);
//仅仅获取包的名字,package类的实例用于表示一个包的信息
Package pack=cls.getPackage();
String pname=pack.getName();//获取包名
String pname=cls.getPackage().getName();
System.out.println("包名:"+pname);
/*
Method类的实例用于表示某个类上的一个方法,通过方法对象可以得知该方法的一切信息
(访问修饰符,返回值类型,方法名,参数列表等等)
还可以通过方法对象来调用该方法(反射机制重点知识)
*/
//Class上的getMethods方法可以获取类对象所表示的类所有公开方法,(包括从超类继承的)
Method[] methods=cls.getMethods();
for(Method method:methods){
System.out.println(method.getName());
}
}
}
反射机制实例化对象
public class ReflectDemo2 {
public static void main(String[] args)throws Exception {
Person p=new Person();
System.out.Println(p)
}
//1.加载需要实例化对象的类对象
//Class cls=Class.forName("reflect.Person");
Scanner scan=new Scanner(System.in);
System.out.println("请输入类名:");
String classname=Scan.nextLine();
Class cls=Class.forName(classname);
//2.类对象提供了方法,newInstance()可以利用其表示的类公开的无参构造器实例化
Object obj=cls.newInstance();
System.out.println(obj);
/*当我们输入reflect.Reflectemo1(reflect是我们的包名)
注意:这里我们的System.out.println(p);
System.out.println(obj);
这两个输出不是同一个对象,我们可以删除peron类中的toString可以看出,地址不同)
*/
附带person类如下
package reflect;
import reflect.annotations.AutoRunClass;
import reflect.annotations.AutoRunMethod;
/*当前类测试放射机制*/
public class Person {
private String name = "张三";
private int age = 22;
public Person(){}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
private void hehe(){
System.out.println(name+":我是私有方法");
}
public void sayHello(){
System.out.println(name+":hello!");
}
public void sayHi(){
System.out.println(name+":hi!");
}
public void dance(){
System.out.println(name+"正在跳舞");
}
public void sing(){
System.out.println(name+"正在唱歌");
}
public void watchTV(){
System.out.println(name+"正在看电视");
}
public void playGame(){
System.out.println(name+"正在打游戏");
}
public void say(String info){
System.out.println(name+"说:"+info);
}
public void say(String info,int count){
for(int i=0;i<count;i++){
System.out.println(name+"说:"+info);
}
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
使用指定构造器实例化
public class ReflectDemo3 {
public static void main(String[] args) throws Exception {
//Person(String ,int)
Person per=new Person("某某欧某",55);
System.out.println(per);
Class cls=Class.forName("reflect.Person");
//通过参数对象获取其表示的某个类的指定构造器
//cls.getConstructor();//Person
/*Constructor类的每一个实例用于表示某个类上的某一个构造器*/
Constructor c=cls.getConstructor(String.class,int.class);
Object o=c.newInstance("某某欧某",55);
System.out.println(o);
使用反射机制调用方法
public class ReflectDemo4 {
public static void main(String[] args) throws Exception {
Person p=new Person();
p.dance();
/* 1.实例化
Class cls=Class.forName("reflect.Person");
Object obj=cls.newInstance();//Person obj=new Person();
//2.调用方法
//2.1通过类对象获取要调用方法的对象(Method对象)
Method method=cls.getMethod("dance");//dance方法
System.out.println(method);
//2.2通过方法对象来调用该方法
method.invoke(obj);//p.dance*/
//1.实例化
Scanner scan=new Scanner(System.in);
System.out.println("请输入要实例化类的类名");
String className=scan.nextLine();
Class cls=Class.forName(className);
Object obj=cls.newInstance();
//2.调用方法
//2.1通过类对象获取要调用方法的对象(Method对象)
System.out.println("请输入方法名:");
String methodName=scan.nextLine();
Method method=cls.getMethod(methodName);
System.out.println(method);
//2.2通过方法对象来调用该方法
method.invoke(obj);//p.dance
}
}
调用有参数的方法
public class ReflectDemo5 {
public static void main(String[] args) throws Exception {
Person p=new Person();
p.say("大家好!");
p.say("我要循环",5);
//1.进行实例化
Class cls=Class.forName("reflect.Person");
Object obj=cls.newInstance();
//调用方法
Method method2=obj.getClass().getMthod("say",String.class);
method2.invoke(obj,"你好")
Method method=cls.getMethod("say",String.class);
method.invoke(obj,"大家好");
Method method1=cls.getMethod("say",String.class,int.class);
method1.invoke(obj,"想要循环",10);
}
}
变长参数
package reflect;
import java.util.Arrays;
/**
* JDK5之后推出一个特性,变长参数
*/
public class ArgsDemo {
public static void main(String[] args){
/**
* 变长参数是编译器认可的,最终会被编译器改为数组
* 传参数时改为:
* dosome();
* dosome(String[]={"a"});
* dosome(String[]={"a","b"});
* dosome(String[]={"a","b","c","d"});
* dosome(String[]={"a","b","c","d","a","b","c","d"});
* dosome(String[]={"a","b","c","d","a","b","c","d"});
* dosome(String[]={"a","b","c","d","a","b","c","d","d","d","d","d","d","d","d","d"});
*
*
* 方法定义会被改为
* public static void dosome(String[] s){
*
*/
dosome();
dosome("a");
dosome("a","b");
dosome("a","b","c","d");
dosome("a","b","c","d","a","b","c","d");
dosome("a","b","c","d","a","b","c","d");
dosome("a","b","c","d","a","b","c","d","d","d","d","d","d","d","d","d");
}
//变长参数在一个方法中必须是最后一个参数
public static void dosome(String... s){
System.out.println(s.length);
System.out.println(Arrays.toString(s));
}
}
在放射机制面前单列模式会失效用
package reflect;
import java.lang.reflect.Method;
public class ReflectDemo6 {
public static void main(String[] args) throws Exception {
Class cls=Class.forName("reflect.Person");
Object obj=cls.newInstance();
/*
getMethod()和getMethods()可以获取本类所有的公开方法
getDeclaredMethod()和getDeclaredMethods()可以获取被类定义的方法(包括私有方法
)
*/
// Method method=cls.getMethod("hehe");
Method method=cls.getDeclaredMethod("hehe");
method.setAccessible(true);//强行打开范文权限
method.invoke(obj);
method.setAccessible(false);//用后应当及时关闭访问权限
//这意味着单例模式在这个反射机制面前也会失去效果
}
}
单例模式
java2种设计模式之一
使用该模式定义的类全局仅可创建一个实例
如何定义单例模式的类
1.私有化构造器(防止外界通过new来实例化对象)(但是我们的反射机制私有化的也能访问,所以失效).
2,提供一个静态方法用于将当前类实例返回给外界
3,提供一个静态的私有的当前类实例的属性并初始化(确保只有一个实例)
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
}
然后我们再在另外一个同包类中演示单例模式(我们在控制台输出S1与S2是同一个地址)
package com.webserver.test;
/**
* 单例模式
* java23中设计模式之一
* 使用该模式定义的类全局仅可创建一个实例
*/
public class SingletonDemo {
public static void main(String[] args) {
Singleton s1 = Singleton.getInstance();
System.out.println(s1);
Singleton s2 = Singleton.getInstance();
System.out.println(s2);
}
}
//这里控制台输出的s1与s2的地址是相同,就是同一个
反射机制注解
放射机制类上注解
package reflect.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 注解:JDK8之后引入的一个新的特性,我们可以利用注解辅助反射机制
* 注解使用前需要定义
*
* JAVA有几个内置的注解是用来为我们定义自定义注解添加某些特性的
* @Target注解:用于说明我们定义的注解可以被应用在哪些位置。
* * 注解允许的值都被定义在ElementType上。
* * 常见的有:
* * ElementType.TYPE 类上
* * ElementType.Method 方法上
* * ElementType.Field 属性上
* * 等等
* * @Retention注解:用于说明当前注解的保留级别,有三个可选值:
* * RetentionPolicy.SOURCE 注解仅保留在源码文件中
* * RetentionPolicy.CLASS 注解会被保留在字节码文件中,但是不可被反射机制访问。这些也是注解的默认保留级别
* * RetentionPolicy.RUNTIME 注解会被保留在字节码文件中且可以被反射机制访问。
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoRunClass {
}
在放射机制中操作注解
import reflect.annotations.AutoRunClass;
/**
* 反射机制中操作注解
*/
public class ReflectDemo7 {
public static void main(String[] args) throws Exception {
Class cls=Class.forName("reflect.Person");
//判断当前cls(表示的Person类)所表示的类是否被注解@AutoRunClass标注
if(cls.isAnnotationPresent(AutoRunClass.class)){
System.out.println("被注解了");
}else {
System.out.println("没有被注解...");}
}
}
此时的Person类代码如下
package reflect;
import reflect.annotations.AutoRunClass;
import reflect.annotations.AutoRunMethod;
/**
* 使用当前类测试反射机制
*/
@AutoRunClass
public class Person {
.....
}
放射机制方法注解
package reflect.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoRunMethod {
/*
注解中可以添加参数:格式为:
参数类型 参数名() [default 默认值]
default 可以为参数设定默认值,当使用注解不传递参数时则采取默认值。
当注解只有一个参数时。参数名建议选取"value"。这样在使用注解时便于传递参数
例如:@AutoRunMethod(22)
正常使用注解时。参数传递格式为@AutoRunMethod(value=22)。即:参数名=参数值
参数的顺序可以和定义时不一样
*/
int value() default 1;
/*
多个参数时,传参顺序不讲究:
@AutoRunMethod(name="world",num=6)
@AutoRunMethod(num=6,name="world")
*/
/* int value() default 1;
String name() default "abc";*/
}
此时的Person类
package reflect;
import reflect.annotations.AutoRunClass;
import reflect.annotations.AutoRunMethod;
/**
* 使用当前类测试反射机制
*/
@AutoRunClass
public class Person {
private String name = "张三";
private int age = 22;
public Person(){}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
private void hehe(){
System.out.println(name+":我是私有方法");
}
@AutoRunMethod(5)
public void sayHello(){
System.out.println(name+":hello!");
}
@AutoRunMethod()
public void sayHi(){
System.out.println(name+":hi!");
}
@AutoRunMethod()//@AutoRunMethod()那么value采取默认值
public void dance(){
System.out.println(name+"正在跳舞");
}
@AutoRunMethod//@AutoRunMethod那么value依然采取默认值
public void sing(){
System.out.println(name+"正在唱歌");
}
public void watchTV(){
System.out.println(name+"正在看电视");
}
public void playGame(){
System.out.println(name+"正在打游戏");
}
public void say(String info){
System.out.println(name+"说:"+info);
}
public void say(String info,int count){
for(int i=0;i<count;i++){
System.out.println(name+"说:"+info);
}
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
获取注解参数
package reflect;
import reflect.annotations.AutoRunMethod;
import java.lang.reflect.Method;
/**
* 获取注解参数
*/
public class ReflectDemo8 {
public static void main(String[] args) throws Exception {
Class cls=Class.forName("reflect.Person");
Method method=cls.getDeclaredMethod("sayHello");
if(method.isAnnotationPresent(AutoRunMethod.class)){
//获取该方法对象所表示的方法上的特定注解
AutoRunMethod arm=method.getAnnotation(AutoRunMethod.class);
//通过注解对象获取参数value的值
int value=arm.value();
System.out.println("参数:"+value);
}
}
}
对于反射机制我们看几个测试代码,还是以上面代码中的Person为基础,然后我在添加一个Student类.
package reflect;
import reflect.annotations.AutoRunClass;
import reflect.annotations.AutoRunMethod;
@AutoRunClass
public class Student {
@AutoRunMethod
public void study(){
System.out.println("学生: good good study ,day day up");
}
@AutoRunMethod(10)//这个方法调用10次
public void playGame(){
System.out.println("在打游戏");
}
}
案例一:利用放射机制调用Person中所有无参构造方法
package reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Map;
/**
* 利用反射机制调用Person中所有无参方法
* 1:加载Person类对象
* 2;实例化Person
* 3:通过类对象获取所有无参方法
* 提示:Method上定义了:int getParameterCount()
* 该方法的作用是获取当前Method表示的方法中有多少个参数
* 4,执行invoke来调用这个方法
*/
public class test {
public static void main(String[] args) throws Exception {
Class cls=Class.forName("reflect.Person");
Object obj=cls.newInstance();
Method[] methods=cls.getDeclaredMethods();
for(Method method:methods){
//判断是否为无参数方法
if(method.getParameterCount()==0&&
//判断是否是公开方法
method.getModifiers()== Modifier.PUBLIC&&
//方法名里面含有字母e的方法
method.getName().contains("e")
){
method.setAccessible(true);
System.out.println(method.getName());
method.invoke(obj);
method.setAccessible(false);
}
}
}
}
实例化与当前类test2在同一个包中的所有类
package reflect;
import java.io.File;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
/**
* 实例化与当前类test2在同一个包中的所有类
* 思路
* 1,首先定位Test2的字节码文件所在的目录(main方法里面的第一行代码)
* 2,通过该目录获取里面所有的.class文件
* 3.由于字节码文件名与类名一致(JAVA语法要求) 。因此可以通过文件名确定类名
* 4,使用Class.forName()加载对应类并实例化
*
* 完成之后,自动调用这些类中所有的无参且公开的方法
*/
public class Test2 {
public static void main(String[] args) throws Exception {
File dir=new File(
//获取当前类所在包的位置
Test2.class.getResource(".").toURI()
);
//添加过滤器获取该字节码中的所有文件
File[] subs=dir.listFiles(f->f.getName().endsWith(".class"));
for(File sub:subs){
String fileName=sub.getName();
// System.out.println(fileName);
String className=fileName.substring(0,fileName.lastIndexOf("."));
// String className=fileName.replace(".calss","");
System.out.println(className);
String packName= Test2.class.getPackage().getName();
className=packName+"."+className;
Class cls=Class.forName(className);
Object obj=cls.newInstance();
Method[] methods=cls.getDeclaredMethods();
for (Method method:methods){
if(method.getParameterCount()==0&&
method.getModifiers()== Modifier.PUBLIC){
System.out.println("无参数且公开方法的名字:"+method.getName());
System.out.println("进行调用:");
method.invoke(obj);
}
}
}
}
}
案例3:实例化与当前类Test3在同一个包中被@AutoRunClass标注的类
package reflect;
import reflect.annotations.AutoRunClass;
import java.io.File;
/**
* 实例化与当前类Test3在同一个包中被@AutoRunClass标注的类
*
*/
public class Test3 {
public static void main(String[] args) throws Exception {
File dir=new File(
Test3.class.getResource(".").toURI()
);
File[] subs=dir.listFiles(f->f.getName().endsWith(".class"));
for(File sub:subs){
String className=sub.getName().replace(".class","");
String packageName=Test3.class.getPackage().getName();
className=packageName+"."+className;
Class cls=Class.forName(className);
if(cls.isAnnotationPresent(AutoRunClass.class)){
Object obj=cls.newInstance();
System.out.println(obj);
}
}
}
}
案例四:自动调用与当前类Test4在同一个包中被注解@AutoRunClass标注的类中那些被@AutoRunMethod标注的方法
package reflect;
import reflect.annotations.AutoRunClass;
import reflect.annotations.AutoRunMethod;
import java.io.File;
import java.lang.reflect.Method;
/**
* 自动调用与当前类Test4在同一个包中被注解@AutoRunClass标注的勒种那些被@AutoRunMethod
* 标注的方法
* 注:
*反射中的几个反射对象:Class,Method,Constructor,Field等都有isAnnotationPresent
* 用来判断是否被某个注解标注了
*/
public class Test4 {
public static void main(String[] args) throws Exception {
File dir=new File(
Test4.class.getResource(".").toURI()
);
File[] subs=dir.listFiles(f->f.getName().endsWith(".class"));
for(File sub:subs){
String className=sub.getName().replace(".class","");
String packgName=Test4.class.getPackage().getName();
className=packgName+"."+className;
Class cls=Class.forName(className);
if(cls.isAnnotationPresent(AutoRunClass.class)){
Object obj=cls.newInstance();
System.out.println("实例化:"+obj);
Method[] methods=cls.getDeclaredMethods();
for (Method method:methods){
if(method.isAnnotationPresent(AutoRunMethod.class)) {
System.out.println("开始调用方法:"+method.getName());
method.invoke(obj);
} } } } }}
案例五:自动调用与当前类Test5在同一个包中被注解@AutoRunClass标注的类中那些@AutoRunMethod标注的范围指定此时(次数没有注解@AutoRunMethod的参数指定)
package reflect;
import reflect.annotations.AutoRunClass;
import reflect.annotations.AutoRunMethod;
import java.io.File;
import java.lang.reflect.Method;
/**
* 自动调用与当前类Test5在同一个包中被注解@AutoRunClass标注的类中那些@AutoRunMethod
* 标注的范范指定次数(次数有注解@AutoRunMethod的参数指定)
*
*/
public class Test5 {
public static void main(String[] args) throws Exception {
File dir=new File(
Test5.class.getResource(".").toURI()
);
File[] subs=dir.listFiles(f->f.getName().endsWith(".class"));
for(File sub:subs){
String className=sub.getName().replace(".class","");
String pacakgeName=Test5.class.getPackage().getName();
className=pacakgeName+"."+className;
Class cla=Class.forName(className);
if(cla.isAnnotationPresent(AutoRunClass.class)){
Object obj=cla.newInstance();
System.out.println("被实例化"+obj);
Method[] methods=cla.getDeclaredMethods();
for(Method method:methods){
if(method.isAnnotationPresent(AutoRunMethod.class)){
AutoRunMethod arm=method.getAnnotation(AutoRunMethod.class);
int value=arm.value();
System.out.println("开始调用这个方法:"+ method.getName()+"()"+value+"次");
for(int i=0;i<value;i++){
method.invoke(obj);
}}}}}}}