建设网站需要哪些编程网站百度不收录的原因
六.JAVA知识点全面总结6泛型反射和注解
1.什么是泛型?可以用在哪里?
2.泛型擦除机制是什么?为什么擦除?
3.通配符是什么?作用是什么?
未更新
1.注解是什么?有什么用?
2.注解的自定义和实现机理是什么?
3.JAVA编译过程是什么?
未更新
1.什么是反射?反射的用处?
2.反射的结构?
3.反射在编译和运行时的理解
未更新
未更新
六.JAVA知识点全面总结6泛型反射和注解
1.什么是泛型?可以用在哪里?
①编译器行为
- 编译器可以对泛型参数进行检测,增强代码的可读性或稳定性。
②用法
- 泛型类: public class Person<T>{ }
- 泛型接口:public interface Person<T>{}
- 泛型方法:public static <E> void print(E[ ] array){}
③项目中
- 通用的返回结果
- 工具类
④泛型代码
- 通用的返回结果
package fanxingtest;public class CommonResult<T> {private String code;//返回状态码private T data;//返回数据private String message;//返回信息public CommonResult(String code, T data, String message) {this.code = code;this.data = data;this.message = message;}public String getCode() {return code;}public T getData() {return data;}public String getMessage() {return message;}public void setCode(String code) {this.code = code;}public void setData(T data) {this.data = data;}public void setMessage(String message) {this.message = message;}@Overridepublic String toString() {return "CommonResult{" +"code='" + code + '\'' +", data=" + data +", message='" + message + '\'' +'}';}
}
2.泛型擦除机制是什么?为什么擦除?
①泛型擦除机制
- 用于编译器类型检查,编译时擦除信息(编译器行为)
- 不是真的泛型,为了兼容老版本,故引入泛型机制但不创建新的类型
②泛型擦除机制简介
- List<T> 编译器擦除为 List
- E[ ] Array 编译器擦除为 Object[] Array
- 可通过反射调用方法来存其他类型(证明是编译时行为),例如List<Integer>通过反射可以存储String类型的变量
③编译过程
- java编译器先检查泛型的类型,然后擦除类型,再编译
④泛型使用限制
- 泛型参数不能传入基本数据类型,需要时Object的子类(擦除为Object)
- 不能用static修饰泛型类中变量或方法(泛型),但可以在静态方法中加入泛型变为泛型方法。
原因是能否通过类.方法调用。如果是泛型类中的静态成员变量,那么调用类.方法仍然确定不了泛型变量。如果是静态方法中加入泛型,那么类.方法也可确定泛型变量。 - 区分泛型类中的方法和静态泛型方法
Class Person<T> {static T eat(){ }}泛型类中的静态方法不可行
Class Person<T>{static <k> void eat(K a){ }}泛型类中的静态泛型方法可行
⑤代码示例
- 泛型擦除机制检验
package fanxingtest;import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Iterator;public class CaChuTest {public static void main(String[] args) throws Exception{HashSet<Integer> integers = new HashSet<>();integers.add(1);//得到hashset中的数为1System.out.println(integers.iterator().next());//利用反射插入String类型的数//此时输出1,"abc",故验证泛型是擦除类型的//注意泛型获得方法对象要加入参数(add是泛型T的方法,运行时擦除为Object类型)Class aClass = integers.getClass();Method add1 = aClass.getDeclaredMethod("add",Object.class);add1.invoke(integers,"abc");Iterator iterator = integers.iterator();while(iterator.hasNext()){System.out.println(iterator.next());}}
}
- 泛型类的静态方法和静态泛型方法
package fanxingtest;public class StaticFanxinTest {}
class People<T>{//编译器报错static T data;//编译器报错static T print(){System.out.println("你好");return T t;}//编译器可运行static <K> void print(K k){System.out.println("你好"+k);}
}
3.通配符是什么?作用是什么?
①通配符使用与子类泛型不能赋值给父类泛型,解决多态不能用于泛型。
List<People> = List<Man> 编译不过 子类泛型不能赋值给父类泛型
List<?> = List<Man> 利用通配符 编译过
②对比
- T 声明变量 类或方法 Object泛型擦除
- ?不能声明 <?> 捕获类型
③通配符的使用
- ? extends A 赋值A或A的子类 可读不可写(子类可能单独的结构)
- ? super B 赋值B或B父类 可写可读(结构都有)
- 注意:赋值时通配符在左,变量在右
- 注意:泛型类型相同,类可使用多态
④代码解释
package fanxingtest;import java.util.*;public class TongPeiFuTest {public static void main(String[] args) {//泛型相同,类不同为子类父类,可以多态HashSet<Integer> integers = new LinkedHashSet<Integer>();//类相同,泛型为子类父类不可以多态,编译出错LinkedHashSet<Exception> integers1 = new LinkedHashSet<RuntimeException>();//类相同,使用通配符,编译过LinkedHashSet<?> integers2 = new LinkedHashSet<RuntimeException>();//通配符Set<? extends HashSet> ext = null;Set<? super HashSet> sut = null;Set<Set> c = null;Set<HashSet> d = null;Set<LinkedHashSet> e = null;ext = c;//编译不过 必须是子类或同级ext = d;//编译过ext = e;//编译过sut = c;//编译过sut = d;//编译过sut = e;//编译不过,必须是父类或同级//可读不可写ext.add(new HashSet());//编译不过Iterator<? extends HashSet> iterator = ext.iterator();iterator.next();//可写可读sut.add(new HashSet());Iterator<? super HashSet> iterator1 = sut.iterator();iterator1.next();}
}
未更新
1.注解是什么?有什么用?
①注解
- 注解用于修饰类,方法,变量,提供在编译或运行时使用
②时间
- 编译器扫描
@override 编译器检查是否重写
@data 编译器加入代码 - 运行期反射处理
@component 通过反射处理具体逻辑(处理器)
③定义
- 系统定义注解
@override
@deprecated - 自定义注解
④元注解(注解的注解)
- @Retention 声明生命周期(自定义注解一般声明为Runtime)
- @Target 声明作用的目标
- @Documented 添加信息到文档
- @Inherited 子类自动继承此注解的注解
2.注解的自定义和实现机理是什么?
①@overide
- 功能:重写的方法
- JVM:在字节码层面实现了该功能
②自定义注解
- 自定义注解
- 实现注解处理逻辑(注解处理器:利用反射处理注解)
- javac注册注解处理器为jvm处理器(会在运行时运行注解处理器)
③代码
- 注解实现检查年龄大于10的孩子有几个
package annotationtest;import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.Field;public class CheckAgeTest {public static void main(String[] args) {MySchoole mySchoole = new MySchoole();try {CheckAge(mySchoole);} catch (Exception e) {e.printStackTrace();}}static void CheckAge(Object o) throws Exception{Class aClass = o.getClass();int num = 0;Annotation[] annotations = aClass.getAnnotations();for(int i = 0;i < annotations.length;i++){if(annotations[i].annotationType()==Check.class){Field[] declaredFields = aClass.getDeclaredFields();for(int j = 0;j<declaredFields.length;j++){if((Integer)declaredFields[j].get(o)>=10){num++;}}}}System.out.println("班级里一共有"+num+"个大于10岁的小孩子");}
}@Retention(RetentionPolicy.RUNTIME)
@interface Check{
}@Check
class MySchoole{int age1 = 11;int age2 = 13;int age3 =9;int age4 = 12;
}
3.JAVA编译过程是什么?
- 解析与填充符号表
- 插入注解处理器并执行注解处理过程
- 分析与字节码的生成
未更新
1.什么是反射?反射的用处?
①反射
- 运行时分析类以及执行类的方法,通过反射获得任何一个类的所有属性和方法,且不知道处理的类是什么(Object)
②用处
- 例子:不想修改代码,只想修改配置
- 例子:代码复用,写一次注解直接多次使用且方便
③举例
- @data lombak(注解 + 反射)
正常写需要每次添加get set方法
反射写,获取注解的类,动态添加get set方法生成.class文件 - properties (配置文件 + 反射)
正常写,每次需要直接修改代码
反射写,每次只需要修改配置。
④代码
- 配置+反射 动态运行代码
//注意:java获取本模块内类的结构用全限定名(包名+类名)(且类必须是public类)
//此时src/下相当于是类路径,即jvm执行class时从类路径下寻找(包名+类名)classname=annotationtest.MyMethod
methodname=saypackage annotationtest;import java.io.FileInputStream;
import java.lang.reflect.Method;
import java.util.Properties;public class PeiZhiTest {public static void main(String[] args) throws Exception{FileInputStream fileInputStream = new FileInputStream("day02/src/annotationtest/method.properties");Properties properties = new Properties();properties.load(fileInputStream);String name1 = properties.getProperty("classname");String name2 = properties.getProperty("methodname");Class aClass = Class.forName(name1);Method declaredMethod = aClass.getDeclaredMethod(name2);declaredMethod.invoke(aClass.newInstance());}}
class MyMethod{void say(){System.out.println("反射调用方法say");}void walk(){System.out.println("反射调用方法walk");}
}
2.反射的结构?
①反射
- Class类:加载到内存的运行时类,都有一个Class的实例
- Class类继承Object类,Class实例能表示运行的Object类
②使用的反射
- 获取Class实例
运行时类的静态属性Class clazz = Person.Class;
运行时类的非静态方法 Class clazz = Person.getClass;
Class类的静态方法 Class clazz = Class.forName(“”); - 创建运行时类的对象
Object o = clazz.newInstance(); - 获取运行时类的结构
clazz.getDeclaredMethods(); - 获取具体方法和具体属性对象
Filed age = clazz.getDeclareFiled(“age”); age.set(o,12);
Method mh = clazz.getDeclareMethod(“show”);show.invoke(o); - 注意
Class的实例不能访问私有结构
Class的实例获得方法时有形参需要加形参,不加默认无形参
3.反射在编译和运行时的理解
①多态
- Person p = new Man()
编译期间:允许
运行期间:子类方法覆盖父类方法(实际上为父类引用指向子类的所有结构)
②反射
- Object obj = clazz.newInstance();method.invoke(obj);
编译期间:运行
运行期间:obj实际上指向为运行时类的实例
③多态+反射
- 即多态+反射可以让父类引用调用子类的私有结构
- 理解:父类引用指向的就是子类的类结构(重写了方法),但是父类引用直接调用子类独有的方法编译不过,故可通过反射调用。
- 代码
package reflecttest;import java.lang.reflect.Method;public class FuZiSiYouTest {public static void main(String[] args) throws Exception{Person p = new Man();Class aClass = p.getClass();Method eat = aClass.getDeclaredMethod("eat");//调用成功 结果为子类私有方法eat.invoke(aClass.newInstance());}
}class Person{}
class Man extends Person{void eat(){System.out.println("子类私有方法");}
}
④总结
- 运行时类Class的实例创建的对象都为Object(编译过)
- 在实际运行时Object指向原本类的结构,但不能直接调用(因为编译不过),需要运行时反射调用(编译过)