声明:
- 该资料来自自己整理。
- 参考书籍
疯狂 Java讲义(第五版) 李刚©著
一、注解
1.1 标记注解(Annotation)
@Override
限定重写父类方法:@Override
class Fruit{
public void info(){
System.out.println("Fruit的info方法...");
}
}
public class AnnotationTest extends Fruit{
//使用@Override指定下面方法必须重写父类方法
@Override
public void info() {
System.out.println("AnnotationTest重写Fruit的info方法...");
}
}
注意:@Override只能修饰方法,不能修饰其它程序元素
@Deprecated
@Deprecated 用于标识某个程序元素(类,方法等)已过时,当其它程序使用已过时的类、方法时,编译期将会给出警告。
@Deprecated注解增加了如下两个属性:(JDK9 新增的)
forRemoval
:该boolean类型的属性指定该API在将来是否会被删除。since
:该String类型的属性指定该API从哪个版本被标记为过时。
class Fruit{
//定义info方法已过时
//since属性指定从哪个版本开始,forRemoval指定该API将来会被删除
//@Deprecated(since="9",forRemoval=true)
@Deprecated
public void info2(){
System.out.println("即将过时的方法...");
}
}
public class AnnotationTest extends Fruit{
public static void main(String[] args) {
//使用@Deprecated方法时将会被编译器警告
new Fruit().info2();
}
}
@SuppressWarnings
抑制编译器警告:@SuppressWarning
@SuppressWarning指示被该注解修饰的程序元素(以及该程序元素中的所有子元素),取消显式指定的编译器警告。@SuppressWarning会一直作用于该程序元素的所有子元素。只能保存在源码级别上。
//关闭整个类里的编译器警告
@SuppressWarnings(value = "unchecked")
public class SuppressWarningTest {
public static void main(String[] args) {
List<String> myList = new ArrayList<>();
}
}
使用@SuppressWarning注解来关闭编译器警告时,一定要在括号里使用 name=value 的形式为该注解的成员变量设置值。
@SafeVarargs
”堆污染“警告与@SafeVarargs。
当把一个不带泛型的独享赋给一个带反省的变量时,往往会发生”堆污染“。例如:
List<String> listStrArray = new List<String>();
List[] listArray = listStrArray;//把List<String>赋给List,发生堆污染。!!!
抑制该堆污染警告有如下三种方式:
- 使用
@SafeVarargs
修饰修饰引发该警告的方法或构造器。JDK 9允许使用该注释修饰私有实例方法。 - 使用
@SuppressWarnings("unchecked")
修饰。 - 编译时使用
-Xlint:varargs
选项。
@FunctionalInterface
@FunctionalInterface就是用来指定某个接口必须是函数式接口。
//@FunctionalInterface只是告诉编译器检查这个接口,
// 保证该接口只能包含一个抽象方法,否则就会编译出错。
@java.lang.FunctionalInterface
public interface FunctionalInterface {
void test();
}
@FunctionalInterface只能修饰接口,不能修饰其他程序元素。
1.2 元注解(meta-annotation)
@Retention【重点】
@Retention:表示需要在什么级别保存该注释信息,用于描述注解的声明周期。
(SOURCE < CLASS < RUNTIME
)
@Target 【重点】
@Target
:用于描述注解的使用范围(即:被描述的注解可用在什么地方)
@Document
@Document
:说明该注释将被包含在javadoc中。
@Inherited
@Inherited
:说明子类可以继承父类中的该注释。
1.3 自定义注解
@interface 关键字
//定义一个简单的注解类型
public @interface Test(){
//注解的参数格式:参数类型 + 参数名();
}
//使用@Test修饰类定义
@Test
public class MyClass{
//使用@Test注解修饰方法
@Test
public void info(){···}
}
1.4 AnnotatedElement 接口
实现类:
1.4.1 提取注解信息
java.lang.reflect 包所提供的反射 API 增加了读取运行时注解的能力。
只有当定义注解时使用了@Rentention(RetentionPolicy.RUNTIME) 修饰,该注解才会在运行时可见,JVM才会在装载 *.class 文件时读取保存在 class 文件中的注解信息。
二、类加载机制
2.1 JVM 和 类
-
当Java程序运行结束时,JVM进程结束,该进程在内存中的状态将会丢失。
-
两个 JVM 之间并不会共享数据。
例如【注意细节】:
public class A {
//定义该类的类变量
public static int a = 6;
}
public class ATest {
public static void main(String[] args) {
A a = new A();
a.a ++;//推荐使用 类名.静态变量 来调用static修饰的静态变量
System.out.println(a.a);//7
}
}
public class ATest2 {
public static void main(String[] args) {
A b = new A();
System.out.println(b.a);//6
//第一次运行JVM结束后,它对A类所做的修改将全部丢失
//第二次运行JVM时将再次初始化A类,所以输出初始值6
}
}
2.2 类的加载(类初始化)步骤
2.2.1 类的加载(步骤一)
一个类在内存中只有一个Class对象;一个类被加载后,类的整个结构都会被封装在Class对象中。
2.2.2 类的连接(步骤二)
2.2.3 类的初始化(步骤三)
当程序主动使用任何一个类时,系统会保证该类以及所有父类(包括直接父类和间接父类)都会先被初始化。
2.3 类初始化的时机
注意:一
class MyTest{
static{
System.out.println("静态初始化块...");
}
//使用一个字符串直接量为static final的类变量赋值
static final String compileConstant = "go go go";
}
public class CompileConstantTest {
public static void main(String[] args) {
//下列代码不会导致初始化MyTest类
System.out.println(MyTest.compileConstant);
}
}
注意:二
2.4 类加载器
【案例】
public class ClassLoaderTest {
public static void main(String[] args) throws ClassNotFoundException {
//获取系统类的加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println("系统类的加载器:"+systemClassLoader);
//获取系统类加载器的父类加载器-->扩展类加载器
ClassLoader parent = systemClassLoader.getParent();
System.out.println("扩展类加载器:"+parent);
//获取扩展类加载器的父类加载器-->根加载器(C/C++)
//根加载器Java不能直接获得
ClassLoader parentRoot = parent.getParent();
System.out.println("根加载器(C/C++):"+parentRoot);
//测试当前类是哪个加载器加载的
ClassLoader classLoader = Class.forName("JVMAndClass.ClassLoaderTest").getClassLoader();
System.out.println("当前类的加载器:"+classLoader);
//测试JDK内置的类是谁加载的
classLoader = Class.forName("java.lang.Object").getClassLoader();
System.out.println("JDK内置的加载器是谁加载的:"+classLoader);
//系统类加载器的加载路径
System.out.println(System.getProperty("java.class.path"));
}
}
三、反射
3.1 简介
获取对象的方式:
3.2 优缺点
优点: 可以实现动态创建对象和编译,体现出很强的灵活性。
缺点: 对性能有影响。
3.3 主要API
java.lang.Class
:代表一个类java.lang.reflect.Method
:代表类的方法java.lang.reflect.Field
:代表类的成员变量java.lang.reflect.Constructor
:代表类的构造器
3.4 获取Class类的实例
【案例】
public class Reflaction01 {
public static void main(String[] args) throws ClassNotFoundException {
Student student = new Student();
System.out.println("这个人是:"+student.name);//这个人是:学生
//方式一:通过对象获得 Class 对象
Class c1 = student.getClass();
System.out.println(c1.hashCode());//1173230247
//方式二:通过 forName 获得
Class c2 = Class.forName("reflaction.Student");
System.out.println(c2.hashCode());//1173230247
//方式三:通过 类名.class 获得
Class c3 = Student.class;
System.out.println(c3.hashCode());//1173230247
//方式四:基本内置类型的包装类都有一个 Type 属性
Class c4 = Integer.TYPE;
System.out.println(c4);//int
//获得父类类型
Class superclass = c1.getSuperclass();
System.out.println(superclass);//class reflaction.Person
}
}
class Person{
public String name;
public Person() {}
public Person(String name) {
this.name = name;
}
@Override
public String toString() {return "Person{" + "name='" + name + '\'' + '}'; }
}
class Student extends Person{
public Student(){ this.name = "学生"; }
}
class Teacher extends Person{
public Teacher() { this.name = "老师"; }
}
3.5 所有类型的Class
//所有类型的Class
public class AllClass {
public static void main(String[] args) {
Class c1 = Object.class;//Object
Class c2 = Comparable.class;//interface
Class c3 = String[].class;//String
Class c4 = int[][].class;
Class c5 = Override.class;//Annotation注解类型
Class c6 = ElementType.class;//enum 类型
Class c7 = Integer.class;//基本数据类型
Class c8 = void.class;//void
Class c9 = Class.class;//Class
System.out.println(c1);
System.out.println(c2);
System.out.println(c3);
System.out.println(c4);
System.out.println(c5);
System.out.println(c6);
System.out.println(c7);
System.out.println(c8);
System.out.println(c9);
//只要元素类型与维度一样,就是同一个 class
int[] a = new int[10];
int[] b = new int[100];
System.out.println(a.getClass().hashCode());//1173230247
System.out.println(b.getClass().hashCode());//1173230247
}
}
3.6 通过反射查看类信息
4.5.1、获得Class对象
4.5.2、从Class中获取信息
返回Class对应类的构造器
返回Class对应类所包含的方法
返回Class对应类所包含的成员变量
返回Class对应类所包含的Annotation
返回Class对应类所包含的内部类、外部类、实现的接口、所继承的父类
返回Class对应类的修饰符、所在包、类名等基本信息
4.5.3、JDK 8新增的方法参数反射
class Test{
public void replace(String str, List<String> list){}
}
public class MethodParameterTest {
public static void main(String[] args) throws NoSuchMethodException {
Class<Test> testClass = Test.class;
//获取Test类的带两个参数的replace()方法
Method replace = testClass.getMethod("replace", String.class, List.class);
//获取指定方法的参数个数
System.out.println("replace方法参数个数:"+replace.getParameterCount());
//获取replace的所有参数信息
Parameter[] parameters = replace.getParameters();
int index = 1;
//遍历所有参数
for (Parameter p : parameters){
if (p.isNamePresent()){
System.out.println("----第"+ index++ +"个参数信息----");
System.out.println("参数名:"+p.getName());
System.out.println("参数类型:"+p.getType());
System.out.println("泛型类型:"+p.getParameterizedType());
}
}
}
}
4.5.4、练习
//定义可重复注解
@Repeatable(Annos.class)
@interface Anno{}
@Retention(value = RetentionPolicy.RUNTIME)
@interface Annos{
Anno[] value();
}
//使用4个注解修饰该类
@SuppressWarnings(value = "unchecked")
@Deprecated
//使用重复注解修饰该类
@Anno
@Anno
public class ClassTest {
//为该类定义一个私有的构造器
private ClassTest(){}
//定义一个有参数的构造器
public ClassTest(String name){
System.out.println("执行有参数的构造器");
}
//定义一个无参数的info方法
public void info(){
System.out.println("执行无参数的info方法");
}
//定义一个有参数的info方法
public void info(String str){
System.out.println("执行有参数的info方法,其参数为:"+str);
}
//定义一个测试用的内部类
class Inner{}
//main
public static void main(String[] args) throws NoSuchMethodException, ClassNotFoundException {
//获取ClassTest对应的Class
Class<ClassTest> classTestClass = ClassTest.class;
System.out.println("ClassTest的带一个参数的构造器如下");
System.out.println("一个参数构造器-->"+classTestClass.getConstructor(String.class));
//获取该Class对象所对应类的public构造器
Constructor<?>[] constructors = classTestClass.getConstructors();
System.out.println("ClassTest的public构造器如下");
for (Constructor constructor : constructors){
System.out.println("constructor-->"+constructor);
}
//获取该Class对象所对应类的全部构造器
Constructor<?>[] declaredConstructors = classTestClass.getDeclaredConstructors();
System.out.println("ClassTest的全部构造器如下");
for (Constructor declaredConstructor : declaredConstructors){
System.out.println("declaredConstructor-->"+declaredConstructor);
}
System.out.println("ClassTest的带一个参数的方法如下");
System.out.println("一个参数方法-->"+classTestClass.getMethod("info", String.class));
//获取该Class对象所对应类的public方法
Method[] methods = classTestClass.getMethods();
System.out.println("ClassTest的public方法如下");
for (Method method : methods){
System.out.println("method-->"+method);
}
//获取该Class对象所对应类的全部方法
Method[] declaredMethods = classTestClass.getDeclaredMethods();
System.out.println("ClassTest的public方法如下");
for (Method declaredMethod : declaredMethods){
System.out.println("declaredMethod-->"+declaredMethod);
}
//获取该Class对象对应类的全部注解
System.out.println("该Class对象对应类的全部注解如下");
Annotation[] annotations = classTestClass.getAnnotations();
for (Annotation annotation : annotations){
System.out.println("annotation-->"+annotation);
}
System.out.println("该Class元素上的@SuppressWarnings注解为-->"+classTestClass.getAnnotationsByType(SuppressWarnings.class));
//获取该Class对象所对应类的全部内部类
Class<?>[] declaredClasses = classTestClass.getDeclaredClasses();
System.out.println("ClassTest的全部内部类如下:");
for (Class declaredClass : declaredClasses){
System.out.println("declaredClass-->"+declaredClass);
}
//使用Class.forName()方法加载ClassTest的内部类
Class<?> innerClass = Class.forName("reflaction.ClassTest$Inner");
System.out.println("innerClass-->"+innerClass);
//通过getDeclaringClass()访问该类所在的外部类
System.out.println("Inner内部类所在的外部类-->"+innerClass.getDeclaringClass());
System.out.println("ClassTest的包为-->"+classTestClass.getPackage());
System.out.println("ClassTest的父类为-->"+classTestClass.getSuperclass());
}
}
3.7 动态创建对象执行方法
setAccessible()方法
:
public class CreateInstanceUseMethod {
public static void main(String[] args) throws Exception {
//获得Class对象
Class<?> c1 = Class.forName("reflaction.Person");
//构造一个对象
Person p = (Person) c1.newInstance();//本质上是调用了类的无参构造器,没有无参构造器会报错
System.out.println(p);//Person{name='null', id=0}
//通过构造器来创建对象
Constructor<?> constructor = c1.getDeclaredConstructor(String.class);
Person p2 = (Person) constructor.newInstance("孙悟空");
System.out.println(p2);//Person{name='孙悟空', id=0}
//通过反射调用普通方法
Person p3 = (Person) c1.newInstance();
Method setName = c1.getDeclaredMethod("setName", String.class);
setName.invoke(p3,"唐僧");//invoke 激活的意思 invoke(对象,"方法的值");
System.out.println(p3.getName());//唐僧
//通过反射操作属性
System.out.println("----------通过反射操作属性----------");
Person p4 = (Person) c1.newInstance();
Field id = c1.getDeclaredField("id");//私有属性直接访问会报错
//不能直接操作私有属性,我们需要关闭程序的安全检测,属性或者方法的setAccessible(true)
id.setAccessible(true);//关闭安全检测即可访问和操作私有属性
id.set(p4,999);//此时不会报错 java.lang.IllegalAccessException:
System.out.println(p4.getId());
}
}
3.8 性能比较
public class PerformanceTest {
//普通方法创建
static void test1(){
Person p = new Person();
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
p.getId();
}
long endTime = System.currentTimeMillis();
System.out.println("普通方式执行10亿次:"+(endTime-startTime)+"ms");
}
//反射方法创建
static void test2() throws Exception {
Person p = new Person();
Class<? extends Person> c1 = p.getClass();
Method getName = c1.getDeclaredMethod("getName", null);
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
getName.invoke(p,null);
}
long endTime = System.currentTimeMillis();
System.out.println("反射方式执行10亿次:"+(endTime-startTime)+"ms");
}
//反射方法创建 关闭检测
static void test3() throws Exception {
Person p = new Person();
Class<? extends Person> c1 = p.getClass();
Method getName = c1.getDeclaredMethod("getName", null);
getName.setAccessible(true);
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
getName.invoke(p,null);
}
long endTime = System.currentTimeMillis();
System.out.println("反射关闭检测方式执行10亿次:"+(endTime-startTime)+"ms");
}
public static void main(String[] args) throws Exception {
test1();
test2();
test3();
}
}