大数据开发成长之路——Java基础(三)

反射

  • Java除基本类型以外其他都是class(包括interface),class的本质也是数据类型(Type),实例化的时候即是将其赋值给变量
  • class/interface的数据类型是Class,每加载一个class,JVM就为其创建一个Class类型的实例,该实例保存了class的完整信息
  • 如果获取了某个Class的实例(类的实例),即获取到了对应class的所有信息,比如String类,当使用String类时,即获取了他的所有参数方法:
    在这里插入图片描述
  • 通过Class实例获取class信息的方法称为反射(Reflection)
  • 反射的目的是当获得某个Object类实例时,我们可以获取这个class的定义信息
	package Hello;
	public class Student {
	
	    private String name;
	
	    public Student() {
	        this("unnamed");
	    }
	
	    public Student(String name) {
	        this.name = name;
	    }
	
	    public void hello() {
	        System.out.println("Hi, " + name + "!");
	    }
	}
	package Hello;
	public class Main {
	    public static void main(String[] args) throws IllegalAccessException, InstantiationException {
	        // Class
	        Class cls = Student.class;	// 获取Student类的Class
	        System.out.println("class name: " + cls.getName()); // Hello.Student
	        System.out.println("class simple name: " + cls.getSimpleName());    // Student
	        System.out.println("package name: " + cls.getPackage().getName());  // Hello
	        System.out.println("is interface? " + cls.isInterface());   // false
	        // Student s = new Student();
	        Student s = (Student) cls.newInstance();	// 通过Class进行类的实例化
	        s.hello();  // Hi, unnamed!
	    }
	}

JVM总是动态加载class,可以在运行期根据条件控制加载class

	Class cls;
    if (true) {
        cls = Class.forName("Hello.Student");
    } else {
        cls = Class.forName("Hello.Something");    // 虽然这个类不存在
    }

访问字段——field

  • Field对象封装了字段的所有信息
  • 通过Class实例的方法可以获取Field实例
    • getField
    • getFields
    • getDeclaredField // 获取非public字段
    • getDeclaredFields
  • 通过Field实例可以获取字段信息
    • getName
    • getType
    • getModifiers // 修饰类型
  • 通过Field实例可以读取或设置某个对象的字段
    • get(Object instance) / set(Object instance, Object value)
  • 通过设置setAccessible(true)来访问非public字段
// class Main
public static void main(String[] args) throws Exception {
        // field字段
        Student s = new Student();	// 实例化类
        Class cls = s.getClass();	// 类的实例
        // 类的实例的方法获取Field实例
        Field f = cls.getField("name");	// public字段
        // Field f = cls.getDeclaredField("address");	// private字段
        printFieldInfo(f);
        // f.setAccessible(true);	// address为private,必须设置为可访问,f.get()才能执行
        // f.set(s,"shanghai");
      	// System.out.println(f.get(s));
      	
      	// Field f = cls.getDeclaredField("number");	// 同样可以访问static字段
      	// f.setAccessible(true);
      	// f.set(null,123);
      	// System.out.println(f.get(null));		// 不需要指定实例类,null即可
        s.hello();
    }
    
static void printFieldInfo(Field f) {
    System.out.println("field name: " + f.getName());
    System.out.println("field type: " + f.getType());
    System.out.println("field modifier: " + f.getModifiers());
    System.out.println("is public? " + Modifier.isPublic(f.getModifiers()));
    System.out.println("is protected? " + Modifier.isProtected(f.getModifiers()));
    System.out.println("is private? " + Modifier.isPrivate(f.getModifiers()));
    System.out.println("is static? " + Modifier.isStatic(f.getModifiers()));
    System.out.println("is final? " + Modifier.isFinal(f.getModifiers()));
}
// field name: age
// field type: int
// field modifier: 1
// is public? true
// is protected? false
// is private? false
// is static? false
// is final? false
// Hi, unnamed from beijing!
// Student.java
public class Student extends Person implements say {

    public static int number = 0;

    public String name;
    private String address = "beijing";

    public Student() {
        this("unnamed");
    }

    public Student(String name) {
        this.name = name;
        number++;
    }

    public void hello() {
        System.out.println("Hi, " + name + " from " + address + "!");
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}
// Person.java
public class Person {
    public int age;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
// say.java
interface say {
    void hello();
}
  • setAccessible(true)可能会失败,这是因为定义了SecurityManager,该规则阻止对Field设置accessible
  • 但是对于大多数自定义类和第三方类没有此限制

调用方法

  • Method对象封装了方法的所有信息
  • 通过Class实例可以获取Method实例
    • getMethod(name, Class...) // 获取某个public属性的method(包括父类)
    • getDeclaredMethod(name, Class...) // 获取当前类的某个method
    • getMethod() // 获取所有public的method
    • getDeclaredMethod() // 获取当前类的所有method
  • 通过Method可以调用对象的方法
    • invoke(Object instance, Object params)
  • 同样,通过设置setAccessible(true)来访问非public属性方法
    在这里插入图片描述

调用构造方法

  • Constructor对象封装了构造方法的信息
  • 通过Class实例可以获取Constructor实例
    • getConstrcutor(Class...) // 获取某个public属性的constructor
    • getDeclaredConstructor() // 获取某个constructor
    • getConstrcutors(Class...) // 获取所有public属性的constructor
    • getDeclaredConstructors() // 获取所有constructor
    • 不可能获取父类的constructor
// Main.java
public class Main {
	public static void main(String[] args) throws Exception {
		Class cls = Student.class;
		// 类的带参数构造方法,传入参数类型的Class实例
		Constructor c = cls.getDeclaredConstructor(String.class, int.class);
		printConstructorInfo(c);
		c.setAccessible(true);
		Student s = (Student) c.newInstance("Xiao Ming", 12);
		s.hello();
	}

	static void printConstructorInfo(Constructor c) {
		System.out.println(c);
		System.out.println("parameters: " + Arrays.toString(c.getParameterTypes()));
		System.out.println("modifier: " + c.getModifiers());
	}

}
// Student.java
public class Student extends Person implements Hello {
	public static int number = 0;
	public String name;
	private String address = "beijing";

	public Student() {
		this("unnamed");
	}

	public Student(String name) {
		this(name, 20);
	}

	private Student(String name, int age) {
		this.name = name;
		this.age = age;
		number++;
	}

	public void hello() {
		System.out.println("Hi, " + name + " from " + address + "!");
	}

	public String getAddress() {
		return address;
	}

	public void setAddress(String address) {
		this.address = address;
	}

}

获取继承关系

  • 获取父类的Class
	Class sup = Integer.class.getSuperclass();	// Number.class
	Object.class.getSuperclass();		// null
  • 获取当前类直接实现的interface
	Class[] ifs = Integer.class.getInterfaces();	// Comparable.class
  • 判断一个向上转型是否成立
	Number.class.isAssignableFrom(Integer.class);	// true

以上部分基本介绍了反射及相关的方法,通过类的实例Class可以获取属性、方法等信息

注解

  • 注解(Annotation)是Java语言使用工具处理时的标注
  • 如何使用注解由工具决定,例如CLASS类型的注解由操作class类型的工具使用,但很少见;我们一般使用RUNTIME类型的注解,在运行期读取注解
  • 注解本身对代码逻辑没有任何影响
  • 注解可以配置参数,没有指定参数则使用默认值
    在这里插入图片描述
    在这里插入图片描述

定义注解

  • 使用@interface定义注解
  • 注解的参数类似无参数方法
  • 把最常用的参数命名为value
public @interface Report{
	int type()	default 0;
	String level()	default "info";
	String value()	default "";
}
  • 使用@Target定义注解可以被应用于源码的哪些位置,属于元注解
    • ElementType.TYPE // 类或接口
    • ElementType.FIELD // 子段
    • ElementType.METHOD // 方法
    • ElementType.CONSTRUCTOR // 构造方法
    • ElementType.PARAMETER // 方法参数
@Target(ElementType.METHOD)
public @interface Report{
	int type()	default 0;
	String level()	default "info";
	String value()	default "";
}
  • 使用@Retention定义注解的生命周期,属于元注解
    • RetentionPolicy.SOURCE // 仅编译期
    • RetentionPolicy.CLASS // 仅class文件(默认)
    • RetentionPolicy.RUNTIME // 运行期(推荐)
  • 使用@inherited定义子类是否可继承父类定义的注解
@Target(ElementType.TYPE)		// 仅针对@Target为TYPE类型的注解
public @interface Report{		// 仅针对类的继承,对interface的继承无效
	int type()	default 0;
	String level()	default "info";
	String value()	default "";
}

在这里插入图片描述

public class Person {
	@NotNull
	public String name;
	@Range(max = 20)
	public int age;
	public person(@NotNull String name, int age){
		this.name = name;
		this.age = age;
	}
}
// NotNull.java
import java.lang.annotation.ElementType;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER})
public @interface NotNull {
}
// Range.java
import java.lang.annotation.ElementType;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Range{
	int min()	default 1;
	int max()	default 100;
}

处理注解

  • 如何读取RUNTIME类型的注解?使用反射API
public static void main(String[] args) {
	Person p1 = new Person("Roy", 25);
	Person p2 = new Person(null, 15);
	checkPerson(p1);
	checkPerson(p1);
}

static void checkPerson(Person p) throws Exception {
    System.out.println("check "+ p + "..." );
    Class cls = Person.class;
    for (Field f : cls.getFields()) {
        checkField(f,p);
    }
}
static void checkField(Field f, Person p) throws Exception {
    if(f.isAnnotationPresent(NotNull.class)) {	// 注解的反射,判断当前是否使用注解
        Object r = f.get(p);
        if(r == null) {
            System.out.println("Error:Field "+f.getName()+"is null");
        }
    }
    if(f.isAnnotationPresent(Range.class)) {
        Range range = f.getAnnotation(Range.class);	// 使用反射实例化注解
        int n = (Integer) f.get(p);
        if(n<range.min() | n>range.max()) {
            System.out.println("Error:field "+ f.getName() + "is out of range");
        }
    }
}

泛型

  • 为什么使用泛型?

举例来说,ArrayList可以看做是变长数组,内部使用Object[] array,但是如果要存储String类型元素,需要强制转型很不方便。因此,要将ArrayList变成一个模板,ArrayList<T>,根据传入类型的不同,创建不同的ArrayList<>

  • ArrayList<T>实现了List<T>接口,可以向上转型
	List<String> list = new ArrayList<String>()
	// 注:ArrayList<Number>与ArrayList<Integer>没有继承关系
  • 泛型,即是编写模板代码来适应任意类型
  • 使用泛型时,把泛型参数<T>替换为需要的class类型
  • 不指定泛型参数时,会使胃Object类型

编写泛型

在这里插入图片描述

  • 静态方法不能引用泛型类型<Y>,必须定义其他类型<K>来实现泛型

回顾:
静态变量被所有的对象所共享
静态变量和方法先于对象出现,所以习惯上使用类名调用
静态方法可以被继承,但不能被重写

  • 定义泛型类型时可定义多种类型<T,K>

泛型的实现

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

通配符

  • extends通配符
    在这里插入图片描述
  • 注:由于不能确定具体类型,所以不能使用set相关的方法;
    在这里插入图片描述
  • super通配符
// Pair.java
public class Pair<T> {
	private T first;
	private T last;
	
	public Pair(T first, T last) {
		this.first = first;
		this.last = last;
	}
	public T getFirst() {
		return first;
	}
	public void setFirst(T first) {
		this.first = first;
	}
	public T getLast() {
		return last;
	}
	public void setLast(T last) {
		this.last = last;
	}
	public String toString() {
		return "Pair(" + first + ", " + last + ")";
	}

}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 通过比较,我们很容易发现extends与super的区别:
  • 可以理解为extends可以使用返回值,但是不可以为方法提供参数;super可以为方法提供参数,但是不能使用返回值;
    在这里插入图片描述
  • 无限定通配符

相当于加上了extends和super的限制

在这里插入图片描述

  • 不同的通配符总的来说限定了使用泛型时的一些继承关系,只需要知道不同通配符的特点即可:由于类型的不确定(类型擦除),extends不能set,super不能get
  • 部分反射API是泛型:
    在这里插入图片描述
  • 泛型数组

可以声明带泛型的数组,但不能直接使用new创建带泛型的数组

扫描二维码关注公众号,回复: 10184035 查看本文章
Pair<String>[] ps = null;
// 必须通过强制转型实现带泛型的数组
Pair<String>[] ps = (Pair<String>[]) new Pair[2];

在这里插入图片描述
在这里插入图片描述

import java.lang.reflect.Constructor;
import java.util.Arrays;

public class Main {

	public static void main(String[] args) throws Exception {
		Class<String> clazz = String.class;	// 反射
		String s1 = clazz.newInstance();	// 得到实例化
		System.out.println(s1);

		Constructor<String> cons = clazz.getConstructor(String.class);// 构造方法传参
		String s2 = cons.newInstance("Hello");	// 通过构造方法实例化
		System.out.println(s2);

		@SuppressWarnings("unchecked")
		Pair<String>[] ps = (Pair<String>[]) new Pair[2];// 必须通过强制转型实现带泛型的数组
		ps[0] = new Pair<>("a", "b");
		ps[1] = new Pair<>("x", "y");
		System.out.println(Arrays.toString(ps));
	}

}

小结

以上主要总结了Java中反射、注解和泛型的概念和基本操作,泛型是最常见的操作,和反射相关联,需要熟练掌握。可以参看一下 其他的文章进一步了解
注:部分截图来自廖雪峰老师的教程;

发布了9 篇原创文章 · 获赞 24 · 访问量 3734

猜你喜欢

转载自blog.csdn.net/weixin_39757637/article/details/104851167