【Java SE学习笔记】2020-07-24反射和注解

一、类的对象和类对象

类的对象:基于某个类new出来的对象,也叫做实例对象
类对象:每一个类加载到内存后都对应有且只有一个class对象,封装了一个类所有的信息,每一个类都是class类的一个对象(利用-verbose:class获得类的加载过程)。

1、获取类对象

1.通过类的对象,获得类对象
Student s = new Student();
Class c = s.getClass();

2.通过类名获取类对象
Class c = 类名.class;

3.通过静态方法,获得类对象,推荐使用这种方式
Class c = Class.forName("包名.类名");

示例:

/*创建一个类*/
public class Student{
    
    
	private int age=0;
	private String name="";
	public Student(){
    
    }
}
/*通过三种方式获得类的类对象*/
public class GetClass{
    
    
//1.第一种方式
		Student stu = new Student();
		Class<?> c1 = stu.getClass();
		System.out.println(c1.hashCode());
//2.第二种方式
		Class c2 = Student.class;
		System.out.println(c2.hashCode());
//3.第三种方式
		try {
    
    
			Class c3 = Class.forName("GetClassTest.Student");
			System.out.println(c3.hashCode());
		} catch (ClassNotFoundException e) {
    
    
			e.printStackTrace();
		}
}

输出:
366712642
366712642
366712642

2、类对象的常用方法

1. 获取类的名字
public String getName();
2. 获得类所在包的包名
public Package getPackage();
3. 获得父类
public Class<? super T> getSuperclass();
4. 获得接口
public Class<?>[] getInterfaces();
5. 获得构造方法
public Constructor<?>[] getConstructors();
6. 创建该类的实例
public T newInstance();
7. 获得方法(只能获取公开的方法,包括从父类继承的方法)
public Method[] getMethods();
8. 获得声明的方法(包括私有保护的方法,不包括从父类继承的方法)
public Method[] getDeclearedMethods();
9. 获得属性(只能获取公开的属性,包括从父类继承得到的属性)
public Field[] getFields();
10. 获得声明的属性(获取公开的属性,不包括从父类继承得到的属性)
public Field[] getDeclearedFields();

3、获取类的构造方法并创建对象

/*创建一个类*/
public class Student {
    
    
	private int age;
	private String name;
	public Student(int age,String name) {
    
    
		this.age=age;
		this.name=name;
		System.out.println("调用了带参构造函数");
	}
	public Student() {
    
    
		this.age=1;
		this.name="abc";
		System.out.println("调用了不带参构造函数");
	}
	@Override
	public String toString() {
    
    
		return "age:"+age+",name:"+name;
	}
}
/*通过类对象获取构造函数,实例化一个对象*/
public class GetClass {
    
    
	 public static void main(String[] args) throws Exception {
    
    
		 //1.用"包名.类名"创建类对象
		 Class<?> c1 = Class.forName("GetClassTest.Student");
		 //2.获得所有构造方法
		 Constructor<?>[] cons = c1.getConstructors();
		 for(Constructor c:cons) {
    
    
			 System.out.println(c.toString());
		 }
		 //3.利用不带参构造函数,实例化一个对象
		 Constructor<?> con1 = c1.getConstructor();
		 Object obj1 = con1.newInstance();
		 System.out.println(obj1.toString());
		 //4.利用带参构造函数,实例化一个对象
		 Constructor<?> con2 = c1.getConstructor(int.class,String.class);//参数类型和顺序
		 Object obj2 = con2.newInstance(5,"AAA");
		 System.out.println(obj2.toString());
	}
}

输出:
//1.
public GetClassTest.Student(int,java.lang.String)
public GetClassTest.Student()
//2.
调用了不带参构造函数
age:1,name:abc
//3.
调用了带参构造函数
age:5,name:AAA

4、调用类的其它方法

/*创建一个类*/
public class Student {
    
    
	private int age;
	private String name;
	public Student(int age,String name) {
    
    
		this.age=age;
		this.name=name;
		System.out.println("调用了带参构造函数");
	}
	public Student() {
    
    
		this.age=1;
		this.name="abc";
		System.out.println("调用了不带参构造函数");
	}
	@Override
	public String toString() {
    
    
		return "age:"+age+",name:"+name;
	}
	public void suck(int len) {
    
    
		System.out.println("带参的方法");
	}
	private void cum() {
    
    
		System.out.println("私有的方法");
	}
	public static void bump() {
    
    
		System.out.println("静态方法");
	}
}
public class GetClass {
    
    
	 public static void main(String[] args) throws Exception {
    
    
		Class<?> c1 =Class.forName("GetClassTest.Student");
		//1.获得
		Method[] methods = c1.getMethods();
		for(Method method:methods) {
    
    
			System.out.println(method.toString());
		}
		//2.获得声明的方法
		Method[] methods2 = c1.getDeclaredMethods();
		for(Method method:methods2) {
    
    
			System.out.println(method.toString());
		}
		//3.带参方法
		Method m1 = c1.getMethod("suck",int.class);
		Student stu = (Student)c1.newInstance();
		m1.invoke(stu,5);
		//4.获取私有方法
		Method m2 = c1.getDeclaredMethod("cum");
		m2.setAccessible(true);//设置为true才能访问私有方法
		m2.invoke(stu);
		//5.获取静态方法
		Method m3 = c1.getMethod("bump");
		m3.invoke(null);//调用静态方法不需要通过该类的对象
	}
}

输出:
//1.非常多方法,略
//2.
public java.lang.String GetClassTest.Student.toString()
private void GetClassTest.Student.cum()
public static void GetClassTest.Student.bump()
public void GetClassTest.Student.suck(int)
//3.
调用了不带参构造函数
带参的方法
//4.
调用了不带参构造函数
私有的方法
//5.
调用了不带参构造函数
静态方法

5、利用反射实现调用类的方法的通用方法

public class Student {
    
    
	private int age;
	private String name;
	public Student(int age,String name) {
    
    
		this.age=age;
		this.name=name;
		System.out.println("调用了带参构造函数");
	}
	public Student() {
    
    
		this.age=1;
		this.name="abc";
		System.out.println("调用了不带参构造函数");
	}
	@Override
	public String toString() {
    
    
		return "age:"+age+",name:"+name;
	}
	public void suck(int len) {
    
    
		System.out.println("带参的方法");
	}
	private void cum() {
    
    
		System.out.println("私有的方法");
	}
	public static void bump() {
    
    
		System.out.println("静态方法");
	}
}
public class GetClass {
    
    
	public static Object invokeAny(Object obj,String methodName,Class<?>[] types,Object...args)throws Exception{
    
    
		Class<?> c1 = obj.getClass();
		Method method = c1.getMethod(methodName,types);
		return method.invoke(obj, args);
	}
	 public static void main(String[] args) throws Exception {
    
    
		Student stu = new Student();
		invokeAny(stu,"suck",new Class[] {
    
    int.class},5);
	}
}

输出:
调用了不带参构造函数
带参的方法

6、获取类的属性

/*Student类同上*/
public class GetClass {
    
    
	 public static void main(String[] args) throws Exception {
    
    
		 Class c1 = Class.forName("GetClassTest.Student");
		 //1.获得声明的所有属性
		 Field[] fields = c1.getDeclaredFields();
		 for(Field f:fields) {
    
    
			 System.out.println(f.toString());
		 }
		 //2.修改属性
		 Field f1 = c1.getDeclaredField("age");//对应实例的一个属性
		 f1.setAccessible(true);//因为属性是私有的所以要修改为true
		 Student stu = (Student)c1.newInstance();//实例化一个对象
		 f1.set(stu, 16);//设置对象的属性值,同理有get()方法获得属性值
		 System.out.println(stu.toString());
	}
}

输出:
//1.
private int GetClassTest.Student.age
private java.lang.String GetClassTest.Student.name
//2.
调用了不带参构造函数
age:16,name:abc

二、工厂设计模式

设计模式:特定问题的固定解决方法。使用设计模式是为了可重用代码,让代码更容易被理解,保证代码可靠性、重用性。
工厂模式:主要负责对象创建的问题。其中一个重要原则是“开闭原则”,对拓展开放,对修改关闭。
工厂模式下一般分为四个部分:父类、子类、工厂、客户。

扫描二维码关注公众号,回复: 13114793 查看本文章

示例:

父类:
public interface USB {
    
    
	public void service();
}
子类1public class Mouse implements USB{
    
    
	@Override
	public void service() {
    
    
		System.out.println("鼠标正在工作");
	}
}
子类2public class Board implements USB{
    
    
	@Override
	public void service() {
    
    
		System.out.println("键盘正在工作");
	}
}
工厂:
public class USBFactory{
    
    
	public static USB createUSB(int type){
    
    
		USB usb = null;
		if(type==1){
    
    
			usb = new Mouse();
		}else if(type==2){
    
    
			usb = new Board();
		}
		return usb;
	}
}
客户:
public class Client{
    
    
	public static void main(String[] args){
    
    
		System.out.println("选择1购买鼠标,选择2购买键盘");
		Scanner input = new Scanner(System.in);
		int choice = input.nextInt();
		USB usb = USBFactory.createUSB(choice);
		usb.service();
	}
}

运行
选择1购买鼠标,选择2购买键盘
输入1,输出:鼠标正在工作
输入2,输出:键盘正在工作

但是上述例子如果要增加子类,则需要修改工厂程序,可以利用反射简化上述过程。需要先创建一个properties文件:

1=Factory.Mouse
2=Factory.Board

修改工厂程序的createUSB方法:

public class USBFactory {
    
    
	public static USB createUSB(String type) throws Exception{
    
    
		USB usb = null;
		Class<?> c = Class.forName(type);
		usb = (USB)c.newInstance();
		return usb;
	}
}

修改客户程序:

public class Client {
    
    
	public static void main(String[] args) throws Exception {
    
    
		System.out.println("选1 购买鼠标,选2购买键盘");
		Scanner input = new Scanner(System.in);
		String choice = input.next();
		Properties pro = new Properties();
		FileInputStream fis = new FileInputStream("src/Factory/usb.properties");
		pro.load(fis);
		fis.close();
		USB usb = USBFactory.createUSB(pro.getProperty(choice));
		if(usb!=null) {
    
    
			System.out.println("购买成功");
			usb.service();
		}
		else {
    
    
			System.out.println("购买失败");
		}
	}
}

运行结果也是相同的,但需要扩展子类的种类的话只需要创建子类后在properties文件中增加条目,代码重用度提高。

三、单例模式

单例:只允许创建一个该类的对象
方式1:饿汉式单例(类加载时创建,天生线程安全)
创建过程:①、首先创建一个常量;②、构造方法改成私有的,类外部不能创建对象;③、通过一个公开的方法,返回这个对象
优缺点:线程安全,生命周期太长,浪费空间,不使用也会实例化。

/*创建一个类*/
public class SingleTon {
    
    
	private static final SingleTon instance = new SingleTon();
	private SingleTon() {
    
    }//私有构造方法,禁止其他类创建该类的对象
	public SingleTon getSingleTon() {
    
    
		return instance;
	}//提供该类的对象的访问
}
/*测试*/
public class SingleTest {
    
    
	public static void main(String[] args) {
    
    
		for(int i=0;i<3;i++) {
    
    
			new Thread(new Runnable() {
    
    
				@Override
				public void run() {
    
    
					System.out.println(SingleTon.getSingleTon().hashCode());
				}
			}).start();
		}
	}
}

输出:
1680256105
1680256105
1680256105

方式2:懒汉式单例(使用时创建,线程不安全,加同步)
创建过程:①、首先创建一个对象,赋值为null;②、构造方法改成私有的,类外部不能创建对象;③、通过一个公开的方法,返回这个对象。
优缺点:生命周期比较短,节省空间,有线程安全,可以通过同步方法和同步代码块解决这个问题。

/*创建一个类*/
public class SingleTon {
    
    
	private static SingleTon instance = null;
	private SingleTon() {
    
    }
/*使用synchronized保证同步*/
	public static synchronized SingleTon getSingleTon() {
    
    
		if(instance==null) {
    
    
			instance = new SingleTon();
		}
		return instance;
	}
}
/*测试代码不变*/

输出:
1311878651
1311878651
1311878651

方式3:懒汉式单例(使用时创建,线程安全,静态内部类写法)

/*创建一个类*/
public class SingleTon {
    
    
	private SingleTon() {
    
    }
	private static class Holder{
    
    
		static SingleTon s = new SingleTon();//创建一个静态对象
	}
	public static SingleTon getSingleTon() {
    
    
		return Holder.s;
	}
}
/*测试代码不变*/

输出:
2003228057
2003228057
2003228057

四、枚举

什么是枚举?枚举是一个引用类型,枚举是一个规定了取值范围的数据类型。枚举变量不能使用其他的数据,只能使用枚举中常量赋值,提高程序安全性,使用的是unum关键字。
①、枚举必须包含枚举常量,可以包含属性、方法、私有构造方法;
②、枚举常量必须写在前面,多个常量之间用逗号隔开,最后分号可写可不写。
③、枚举的本质:枚举是一个终止类(final class),并继承Enum抽象类,枚举中常量是当前类型的静态常量。
例如:

public enum Gender {
    
    
	MALE,FAMALE;
}
public class GenderTest {
    
    
	public static void main(String[] args) {
    
    
		Gender gender = Gender.FAMALE;
		System.out.println(gender.toString());
	}
}

输出:
FEMALE

上面的Gender.class利用XJad反编译工具反编译后的java文件内容:

public final class Gender extends Enum
{
    
    
	public static final Gender MALE;
	public static final Gender FAMALE;
	private static final Gender ENUM$VALUES[];
	private Gender(String s, int i)
	{
    
    
		super(s, i);
	}
	public static Gender[] values()
	{
    
    
		Gender agender[];
		int i;
		Gender agender1[];
		System.arraycopy(agender = ENUM$VALUES, 0, agender1 = new Gender[i = agender.length], 0, i);
		return agender1;
	}
	public static Gender valueOf(String s)
	{
    
    
		return (Gender)Enum.valueOf(enumtest/Gender, s);
	}
	static 
	{
    
    
		MALE = new Gender("MALE", 0);
		FAMALE = new Gender("FAMALE", 1);
		ENUM$VALUES = (new Gender[] {
    
    
			MALE, FAMALE
		});
	}
}

枚举类型经常搭配switch一起使用:

public enum Season {
    
    
	SPRING,SUMMER,FALL,WINTER;
}
public class SeasonTest {
    
    
	public static void main(String[] args) {
    
    
		Season season = Season.WINTER;
		switch(season) {
    
    
		case SPRING:
			System.out.println("spring");break;
		case SUMMER:
			System.out.println("summer");break;
		case FALL:
			System.out.println("fall");break;
		case WINTER:
			System.out.println("winter");break;
		}
	}
}

输出:
winter

五、注解

1、什么是注解?注解是代码里的特殊标记,程序可以读取注解,一般用来代替配置文件。可以通过反射去得到类里的注解,以决定去运行什么类。
定义注解用@interface关键字,注解里面只能包含属性。
注解的属性类型String类型、基本数据类型、Class类型、枚举类型、注解类型以及上述类型的一维数组

举个例子:

public @interface MyAnnotation {
    
    
	//可以设置默认值,属性后面需要加圆括号
	String name() default "ABC";
	int age() default 123;
}

上述MyAnnotation.class反编译后的内容,继承了Annotation接口,原先的属性变成方法:

import java.lang.annotation.Annotation;
public interface MyAnnotation extends Annotation
{
    
    
	public abstract String name();
	public abstract int age();
}

2、元注解:同来描述注解的注解
@Retention:用于指定注解可以保留的域
RetentionPolicy.CLASS:注解记录在class文件中,运行java程序时,JVM不会保留,这是默认值。
RetentionPolicy.RUNTIME:注解记录在class文件中,运行java程序时,JVM会保留注解,程序可以通过反射获取该注释。
RetentionPolicy.SOURCE:编译时直接丢弃这种策略的注释。

示例:

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/*如果没有设置RUNTIME,获取的注解就会变成null*/
@Retention(value=RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    
    
	String name();
	int age();
	String sex();
}
public class Person {
    
    
	@MyAnnotation(name="abc",age=10,sex="female")
	public void show(String name,int age,String sex) {
    
    
		System.out.println(name+" "+age+" "+sex);
	}
}
public class Demo {
    
    
	public static void main(String[] args) throws Exception {
    
    
		Class<?> c1 = Class.forName("annotationtest.Person");
		Method method = c1.getMethod("show", String.class,int.class,String.class);
		MyAnnotation myanno = method.getAnnotation(MyAnnotation.class);//得到方法上的注解
		System.out.println(myanno.name());
		System.out.println(myanno.age());
		System.out.println(myanno.sex());

		Person person = (Person)c1.newInstance();
		method.invoke(person, myanno.name(),myanno.age(),myanno.sex());
	}
}

输出:
abc
10
female
abc 10 female

@Target:指定注解用于修饰类的哪个成员
示例:

/*此时注解只能应用在类的方法上*/
@Retention(value=RetentionPolicy.RUNTIME)
@Target(value= {
    
    ElementType.METHOD})
public @interface MyAnnotation {
    
    
	String name();
	int age();
	String sex();
}

关于反射的简单学习到此结束~

猜你喜欢

转载自blog.csdn.net/weixin_43665650/article/details/107440980