2019北京培训:反射及单例设计模式

一、什么是反射?

反射是Java中的一种底层技术,可以动态(在运行时)的获得一个类的信息。

二、反射的核心

反射的核心是类对象。当代码被编译成.class文件后,每个类的信息都会产生一个.class文件,JVM到classpath中把class文件中所保存的类的信息加载到JVM内部的过程,叫做类加载。

由于JVM虚拟机中只能加载基本数据类型和对象,所以类加载后便成为类对象(将一个类的信息封装到一个对象中),每个类只有唯一的一个类对象。(类对象是类加载的结果,保存一个类的信息

类的对象和类对象的区别:类的对象:一个学生对象保存一个学生的信息

类对象:保存了学生的类的信息,类对象也是对象,属于Class类

三、获取类对象的方式

(1)使用类的对象获得类对象

(2)使用类获得类对象

(3)使用类的完全限定名获得类对象

Student s1 = new Student("zhangsan", 20);
		// 1使用类的对象获得类对象
		Class c1 = s1.getClass();
		// 2 使用类获得类对象
		// Class c4=int.class;//基本类型也有类对象
		Class c2 = Student.class;
		// 3使用类的完全限定名获得类对象
		Class c3 = Class.forName("day1.reflect.Student");

c1 c2 c3都是Student类的类对象,所以c1==c2==c3,为同一个类对象

四、通过类对象操作类的相关信息

(1)通过类对象(Class类的对象)获取类名、父类名、实现的接口名

                // 使用类对象获得类名
		System.out.println(c1.getName());
		System.out.println(c1.getSimpleName());
		// 获得父类
		System.out.println(c1.getSuperclass().getName());
		// 获得实现的接口
		for (Class c : c1.getInterfaces()) {
			System.out.println(c.getName());
		}

(2)通过类对象获取包名

Package p = c1.getPackage();
System.out.println(p.getName());

(3)通过类对象获取该类的方法

                // 获得类的方法
		// 获得所有可以调用的方法
		for (Method m : c1.getMethods()) {
			System.out.println(m.getName());
		}
		// 获得本类中所有声明的方法
		System.out.println("------------------");
		for (Method m : c1.getDeclaredMethods()) {
			System.out.println(m.getName());
		}

①getMethods()方法:获取到的是本类所有的非私有方法和从父类继承过来的所有方法

②getDeclaredMethods()方法:获取的是本类的方法(包括私有方法),而不存在从父类继承来的方法。

获得特定的一个方法

// 获得一个方法,需要提供:方法名,形参类型
		Method m1 = c1.getMethod("getName");
		System.out.println(m1);
		Method m2 = c1.getDeclaredMethod("study",String.class);
		System.out.println(m2);

getMethod(方法名,需获取方法的形参列表的每个元素类型所对应的类对象);

(4)获得、设置类的属性和属性值

// 获得类的属性
		for (Field f : c1.getDeclaredFields()) {
			System.out.println(f);
		}

获取私有的属性,需要设置可见性。

// 获得私有的属性name
		Field f1 = c1.getDeclaredField("name");
		// 把私有的属性设置为可访问
		f1.setAccessible(true);
		// 访问属性
		// 设置属性值,第一个参数是给谁设置属性,第二个参数是要设置的属性值
		f1.set(s1, "zhanglaosan");
// 获得属性值,参数为获得谁的属性值
		System.out.println(f1.get(s1));

如果需要获取静态属性,则只需要把形参中的对象填为null

(5)获取类的构造方法

// 获得类的构造方法
		for (Constructor cc : c1.getConstructors()) {
			System.out.println(cc);
		}
		System.out.println("-----------------");
		for (Constructor cc : c1.getDeclaredConstructors()) {
			System.out.println(cc);
		}
// 获得有两个参数的构造方法:String,int
		Constructor con1 = c1.getConstructor(String.class, int.class);

(6)通过类对象进行方法调用

// 使用反射的方式进行方法调用
		// 获得getName方法对应的方法对象
		Method m1 = c1.getMethod("getName");
		// 调用
		// 两个参数,第一个参数是调谁的方法,第二个参数是方法调用时传的参数,可变长
		// 返回值是方法本调用时的返回值
		Object result = m1.invoke(s1);// Object result = s1.getName();
		System.out.println(result);
// 获得要调用的方法对象
		Method m2 = c1.getMethod("setAge", int.class);
		// 使用反射进行方法调用
		m2.invoke(s1, 21);

但当需要调用一些私有方法时,需要设置可访问。

Class c1 = s1.getClass();
		Method m1 = c1.getDeclaredMethod("study");
		// 把私有的方法设置为可访问的
		m1.setAccessible(true);
		m1.invoke(s1);

(7)通过类对象创建对象

①通过类对象直接调用newInstance()方法获得一个对象,返回值为Object类型的对象,但要求该类必须存在公共无参构造器,不然无法创建。

②通过类对象获取构造方法类的对象,调用构造方法类的newInstance()方法,此方法无论有参无参,无论公共还是私有,都可以进行对象的创建。

// 获得类对象
		Class c2 = Teacher.class;
		// 获得私有的无参构造方法
		Constructor con1 = c2.getDeclaredConstructor();
		// 设置可访问性为true
		con1.setAccessible(true);
		// 调用,创建对象
		Object o1 = con1.newInstance();
		System.out.println(o1);

五、单例设计模式

单例设计模式:一个类只允许创建一个对象

(1)饿汉式单例:如果不考虑反射等其他技术的影响,可以使用,可靠

abstract class Student{
	//饿汉式单例
	//abstract类无法创建对象,即保证了即使通过反射这种技术,也无法通过类的构造器去创建对象
	//new Student(){}实则为一个子类的匿名内部类对象,因为此abstract类无任何抽象方法
	//保证了线程安全且效率高,只是随着类的加载对象就已经被创建(即使不使用)占内存空间。
	public static final Student stu = new Student() {};
	
	private Student() {
		
	}
	
}

(2)懒汉式单例:第一次使用对象时创建,需要考虑并发问题,效率低

class Teacher{
	
	//饿汉式单例
	//用volatile关键字修饰保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
	//解决了共享数据的内存可见性问题
	private static volatile Teacher t;
	
	private Teacher() {
	}
	//如不用volatile关键字修饰,则必须把synchronized锁加在方法上
	//(相当于对本方法体加了synchronized(this),又因为此方法为静态方法,故相当于synchronized(Teacher.class))
	public static Object getTeacher() {
		//此if主要判断对象是否被创建
		if(t == null) {
			//t1,t2,t3线程如因争抢资源在此等待,只能有一个线程进入synchronized
			synchronized(Teacher.class){
				//主要是判断在并发时是否创建了对象
				if(t == null) {
					t = new Teacher();
				}
			}
		}
		return t;
	}
}

猜你喜欢

转载自blog.csdn.net/m2606707610/article/details/86481202