Java30反射,注解,自定义junit
Posted 码农编程录
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java30反射,注解,自定义junit相关的知识,希望对你有一定的参考价值。
文章目录
1.Class对象三种获取方式:Class.forName(" ");
package com.itheima02.clazz;
import org.junit.Test;
/*
* 反射前提: Class对象
* 1. 引用类型: Class(大写C和关键字class不一样)
* 2. 回顾: 当程序用到一个类的时候, 会加载这个类进内存(方法区)
* 3. 在运行时, java用Class对象来描述.class文件(就算是一个文件也用对象描述)
* .class文件(硬盘) -> Class对象(内存)
*
* 4. Class对象的特点:
* 1. Class对象的创建:
* 一个.class文件被加载进内存,JVM会创建这个类的Class对象。
* 因为一个.class文件在使用的时候只需要被加载一次, 所以这个.class文件对应的Class对象也只会有一个!!!
*
* 重点: Class对象是JVM创建的, 开发者是无法创建的。(开发者可以new一个object等)
* 一个类的Class对象一般只有一个 (使用: 同步锁 xxx.class对象:别人不能创建,天然唯一安全)
*
* 2. 三种Class对象的获取(不是创建)
* 1. 类名.class
* 2. 对象名.getClass()
* 3. Class.forName(全限定名); -> 加载配置文件
*/
public class ClassDemo {
@Test
public void method01(){
Class<Student> clazz = Student.class;
System.out.println(clazz); //class com.itheima02.clazz.Student //全类名或全限定名 : 包名 + 类名
synchronized (ClassDemo.class){ //当前类名.class -> 锁对象
//这段代码能够运行,意味着当前类一定被加载进内存-> JVM会创建当前类的Class对象
//ClassDemo.class改为string.class浪费内存,因为不一定用到string,没必要加载string类
}
}
@Test
public void method02(){
Student s = new Student();
Class<?> clazz = s.getClass();
System.out.println(clazz); //class com.itheima02.clazz.Student 同上
System.out.println(clazz == Student.class); //true
}
@Test
public void method03() throws ClassNotFoundException {
Class<?> clazz = Class.forName("com.itheima02.clazz.Student");
System.out.println(clazz); //class com.itheima02.clazz.Student 同上
}
}
package com.itheima02.clazz;
import java.util.Objects;
public class Student {
private String name;
@Override
public boolean equals(Object o) {
if (this == o) return true; //this表示student类
// this.getClass() == o.getClass()
// o instanceof Student
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
}
2.反射:clazz.newInstance()
package com.itheima03.reflect;
import org.junit.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/*
* 反射(reflect) : 在程序运行时操作类
* 1. 运行时 : .java源码 -> .class字节码(编译) -> runtime(运行)
* 2. 操作 :
* 3. 类 : .java -> .class(二进制指令让jvm读) -> Class对象(运行时的类的样子)
* 1. 构造方法 / 构造器 Constructor
* 2. 方法 Method
* 3. 属性 Field
* A. 操作构造方法: 以前: 用构造方法来 创建对象
*/
public class ConstructorDemo {
public static void main(String[] args) {
// Person p = new Person(); //Person{name='null',age=0}
Person p = new Person("张三");
System.out.println(p); //Person{name='张三',age=0}
}
//1111111111111111111111111111111111111111111111111111111111111111111111111111111111
@Test
public void method01() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// 反射 : 操作构造方法
//1. 获取Class对象
//2. 再获取Class对象中的构造方法
//3. 使用构造方法创建对象
Class clazz = Person.class;
/*
* Constructor<T> getConstructor(Class<?>... parameterTypes)
* 1. 参数: 需要指定想要获取的构造方法的参数列表 类型
* 2. 返回值: 返回构造方法的对象
*/
//Constructor constructor = clazz.getConstructor(String.class,int.class);
Constructor constructor = clazz.getConstructor(String.class);
/*
* T newInstance(Object ... initargs)
* 1. 参数: 使用构造方法创建对象时传入的实参 (必须跟上面参数列表类型一致)
* 2. 返回值: 返回创建好的实例
*/
Object obj = constructor.newInstance("张三");
System.out.println(obj); //Person{name='张三',age=0}
}
//11111111111111111111111111111111111111111111111111111111111111111111111111111111111
@Test
public void method02() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { //简易的api
Class clazz = Person.class;
/* Constructor constructor = clazz.getConstructor();
Object obj = constructor.newInstance();
System.out.println(obj); //Person{name='null',age=0} */
//Class对象获取空参构造,并创建实例,如下简单,等同于上面两行//(JavaBean要求: 1. private属性 2. public 空参构造 3. public get set方法)
Object obj = clazz.newInstance();
System.out.println(obj); //Person{name='null',age=0}
}
//11111111111111111111111111111111111111111111111111111111111111111111111111111111111
@Test
public void method03() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { //暴力反射: 知道(可以访问private方法)
Class clazz = Person.class;
/*
* getConstructor() : 只能获取public修饰的构造
* getDeclaredConstructor() : 获取所有权限的构造(private也可以)
*/
// Constructor constructor = clazz.getConstructor(String.class, int.class);
Constructor constructor = clazz.getDeclaredConstructor(String.class, int.class);
constructor.setAccessible(true); //暴力反射: 权限设置public //上面能拿到私有,但是不能调用,只能person本类内部调用,所以加这行可以调用
Object obj = constructor.newInstance("李四", 18);
System.out.println(obj); //Person{name='李四',age=18}
}
}
package com.itheima03.reflect;
public class Person {
public String name;
private int age;
public Person(){
}
public Person(String name){
this.name = name;
}
private Person(String name,int age){ //注意private,用暴力反射
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\\'' +
", age=" + age +
'}';
}
public void speak(String msg){
System.out.println("speak:" + msg);
// return 1;
}
public void speak(double msg){
System.out.println("speak:" + msg);
}
private void show(int year){
System.out.println("show:" + year);
}
}
package com.itheima03.reflect;
import org.junit.Test;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
// B.方法的反射(反射中最重要的是构造方法和普通方法反射,没有属性)
public class MethodDemo {
public static void main(String[] args) {
Person p = new Person();
// Person p = null; //speak方法设为静态,不报错
p.speak("呵呵");
System.out.println(number); //speak:呵呵
}
//1111111111111111111111111111111111111111111111111111111111111111111111111111111111
@Test
public void method01() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
//1. 获取Class对象
//2. 获取方法 .getMethod
//3. 调用方法 .invoke
Person p = new Person();
Class<?> clazz = p.getClass();
/*
* Method getMethod(String name, Class<?>... parameterTypes)
* 1 .参数
* 1. name 方法名
* 2. name 方法名的参数列表
* 2. 返回值: 获取的这个方法
*/
Method method = clazz.getMethod("speak", String.class);
/*
* Object invoke(Object obj, Object... args)
* 1. 参数
* 1. obj : 调动此方法的对象,speak前加一个static,null也可以
* 2. args : 调用此方法传入的实参
* 2. 返回值: 调用此方法产生的返回值,如果此方法没有返回值,将会null
* */
Object result = method.invoke(p, "嘻嘻");
//Object result = method.invoke(null, "嘻嘻"); //speak方法设为静态,不报错
System.out.println(result); //speak:嘻嘻
}
//11111111111111111111111111111111111111111111111111111111111111111111111111111111
@Test
public void method02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Person p = new Person();
Class<? extends Person> clazz = p.getClass();
Method show = clazz.getDeclaredMethod("show", int.class); //暴力反射
show.setAccessible(true); //临时修改权限
show.invoke(p,15); //show:15
}
}
package com.itheima03.reflect;
import org.junit.Test;
import java.lang.reflect.Field;
//属性的反射一般不用,关于属性值设置通过反射get和set方法,又回到了方法的反射
public class FieldDemo {
public static void main(String[] args) {
Person p = new Person();
p.name = "啦啦";
System.out.println(p); //Person{name='啦啦',age=0}
}
//111111111111111111111111111111111111111111111111111111111111111111111111111111111
@Test //下面写那么多代码为了实现上面3行
public void method01() throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, InstantiationException {
//1.获取Class对象
//2.获取属性对象
//3.操作属性,get / set
Class<?> clazz = Class.forName("com.itheima03.reflect.Person");
Object p = clazz.newInstance();
/*
* Field getField(String name)
* 1. 参数: 属性名
* 2. 返回值: 通过属性名找到的属性对象
*/
Field age = clazz.getDeclaredField("age");
age.setAccessible(true);
/*
* set(Object obj, Object value)
* 1. 设置属性的对象
* 2. 设置的属性值
*/
age.set(p,18); //很少用,一般get和set方法,又回到方法的反射
System.out.println(p);//Person{name='null',age=18}
}
}
3.注解与元注解:注释
package com.itheima04.annotation;
import java.util.Date;
/*
* 注解: annotation ( 注释;注解,java 四大类型)
* 1. 注释: 一般是给人看的代码解释性说明 (不参与编译, 运行)
* 注解: 给JVM看的代码 解释性说明(可能参与编译或运行)
* 2. 常见
* 1. @Override : 检测此方法是否是重写方法
* 2. @FunctionalInterface:此接口是否是函数式接口
* 3. @Deprecated : 加上方法上,声明此方法过时了(这个方法有更好的替代方案),但是还能用
*/
public class Demo01 {
public static void main(String[] args) {
new MyRunnable().run();
}
}
class MyRunnable implements Runnable{
@Deprecated
@Override
public void run() {
System.out.println("---------");
}
}
package com.itheima04.annotation;
import java.util.Date;
/*
* 注解的基本语法
* 1. 关键字: @interface
* 2. 属性: String name() default "yy"; (注解属性类型: 8大基本类型和String,枚举,注解,Class 。 以及这12种类型的数组。不包括Date等)
* 3. 注解没有方法
*
* 注解的使用: 可以在代码上加注解
* 定义@Override :
* 1. 适用范围 : 方法上
* 2. 生命周期(从创建到销毁):
* SOURCE : 源码 (@Override写在这个时候,没必要到下面两个阶段,节省运行时内存)
* CLASS : 编译后
* RUNTIME : 运行时
* 比如: IO流(过河拆桥,用完后即时释放), 泛型(泛型只在编译阶段,运行时也被擦除)
*/
@MyAnno
public class Demo02 {
@MyAnno
public static void main(@MyAnno String[] args) {
@MyAnno
int i;
}
}
@interface MyAnno{
String name() default "yy";
int[] array();
}
Player表示玩家,Data表示注册账号日期
package com.itheima04.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/*
* 元注解: 用来描述注解的注解 (初始的注解)。 元数据 : 诠释数据的数据(如右击一个图片属性查看)
* name = 张裕
* 属性(元数据) = 值(数据)
*
* 两个元注解
* 1. @Target : 适用范围
* 1. ElementType.Type : 四大java类型上可用
* 2. ElementType.Method : 方法上可用
* 3. ElementType.Constructor : 构造上可用
* 4. ElementType.Field:属性上可用
*
* 2. @Retention : 保留(生命周期)。源码,编译,运行
* 1. SOURCE :编译后target文件夹下YourAnno.class中没有@YourAnno,override注解就是保留在SOURCE源码
* 2. CLASS :编译后target文件夹下YourAnno.class中有@YourAnno,和上面源码一样
* 3. RUNTIME : 常用,加载到jvm里注解还在,可用反射clazz.getDeclaredAnnocation和
* clazz.isAnnotationPresent(YourAnno.class)查看有没有。
*
* 注解的使用:
* 1. 可以放在代码上
* 2. 使用注解的时候,如果有属性没赋值,必须赋值
* 3. 特殊: 如果注解中的属性有且仅有一个未赋值,并且属性名为value,在赋值的时候,value= 可省略
*/
//@YourAnno(name="yy") //注解的使用
//@YourAnno(value="yy") //把String value();放开未赋值,对应上面注释中注解使用中的2
//@YourAnno("yy") //把String value();放开未赋值,对应上面注释中注解使用中的3
public class Demo03 {
@YourAnno
public static void main(String[] args) {
}
}
//1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
//@Target(value={ElementType.METHOD,ElementType.TYPE}) //枚举名.属性 //如下图
//@Target({ElementType.METHOD,ElementType.TYPE}) //缩略写法,override只适用于方法,类型不能用,如下改进
@Target(ElementType.METHOD)//在注解中,如果数组只有一个元素,{}是可以省略的
@Retention(RetentionPolicy.SOURCE)
@interface YourAnno{
String name() default "xx";
// String value(); //属性,类似方法
}
推测springboot项目如何利用注解运行起来的呢?
比如spring的bean怎么初始化的呢?
为什么能在一个类上写一个@component,@Service, @controller这些注解就能将这个类作为单例spring bean注入到spring工厂中去呢?
如下.run(本类)。
@SpringBootApplication默认扫描本包下所有类如下。
如下也new Svc1放入spring工厂,这就是注解和反射配合使用的方式。注解本身没有太大作用,注解源码就几行。发挥作用的是扫描注解的反射部分,针对注解的类型和注解里的字段进行相应的配置。
4.自定义junit测试框架并打jar包:method.isAnnotationPresent
package com.itheima05.custom;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/*
* 思路: @MyTest替换@Test
* 1. 自定义MyTest注解
* 1. 属性 : 无 (有属性必须要赋值,下面没用到MyTest里没有赋值)
* 2. 元注解: 1. 指定作用域: Method
* 2. 生命周期: runtime(注解保留到运行时期才能碰到反射)
*
* 2. 反射 : 在运行时操作类
* 需求: 在方法上 加了 MyTest注解的方法能够像main方法一样运行,所以用到反射
* 1. 获取TestDemo 的Class对象
* 2. 获取类中的所有public方法
* 3. 判断每个方法上是否有注解MyTest,如果有,就运行
*/
public class CustomDemo {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, InvocationTargetException {
Class<?> clazz = Class.forName("com.itheima05.custom.TestDemo");
Object obj = clazz.newInstance();
Method[] methods = clazz.getMethods(); //注意是getMethods最后有一个s,返回数组
for (Method method : methods) {
boolean result = method.isAnnotationPresent(MyTest.class); // 方法上是否有指定注解,如果有返回true
if(result){
// method.invoke(obj); //普通方法调用,第一个参数是调用此方法的对象 //这行不用线程
// 如下有异常时程序要不影响其他方法输出,所以每个方法用一个线程
new Thread(() -> {
try {
method.invoke(obj);
}catch (Exception e) {
e.printStackTrace();
}
}).start();
}
}
}
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface MyTest{
}
package com.itheima05.custom;
public class TestDemo {
public void method00(){
System.out.println("方法0");
}
@MyTest
public void method01(){
System.out.println("测试方法一");
}
@MyTest
public void method02(){
int i = 1/0; //异常不能影响其以上是关于Java30反射,注解,自定义junit的主要内容,如果未能解决你的问题,请参考以下文章