javaSE(十一)

版权声明:博主原创,转发请私信。 https://blog.csdn.net/YooFale/article/details/83075127

1.类的加载

类加载到内存中,三步,加载,连接,初始化

加载:将硬盘上的class文件读入到内存中,为其创建class对象

连接:验证 准备 解析

初始化:

加载时机:

创建类的实例

访问类的静态变量

调用类的静态方法

使用反射方式来强制创建某个类或者接口对应的java.lang.class对象

初始化某个类的子类

直接使用java.exe命令来运行某个主类

2.类加载器的概述和分类

类加载器负责将.class加载到内存中,并为之生成class对象。

类加载器机制

根类加载器:Bootstrap ClassLoader    java核心类的加载,System.String类等,在rt.jar中

拓展类加载器:Extension ClassLoader   ext目录

系统类加载器:System ClassLoader  负责启动JVm启动时加载来自java命令的class文件,以及classpath指定的jar包和类路径

3.反射

在运行状态中,都能知道这个类的方法和属性。对任何对象,都能调用她的属性和方法。这种动态获取类的属性,调用对象的方法,就是java的反射机制。

要想解剖一个类,就必须获取到该类的字节码文件对象。

而解剖使用的就是class类中的方法,所以先要获取每一个字节码文件对应的class类型的对象。

源文件阶段            Person.java                          class.forName("类名")  读取配置文件

字节码阶段               Person.class                       Person.class                当做静态方法的锁对象

创建对象阶段     Person p = new Person           p.getClass                    判断是否为一个字节码对象(equals方法中常见)

4.反射的常用方法

Class.forName()读取配置文件的举例

public static void main(String[] args) throws Exception {
//		创建榨汁机
		Juicer j = new Juicer();
//		字符流读取配置文件
		BufferedReader br = new BufferedReader(new FileReader("config.properties"));
//		获取该类的字节码文件
		Class clazz = Class.forName(br.readLine());
//		创建实例对象
		Fruit instance = (Fruit) clazz.newInstance();
		j.run(instance);
	}

通过反射获取有参构造方法:

public static void main(String[] args) throws Exception {
		Class clazz = Class.forName("com.heima.bean.Person");
		Constructor c = clazz.getConstructor(String.class, int.class);
		Person newInstance = (Person) c.newInstance("张三", 23);
		System.out.println(newInstance);
	}

通过反射获取成员变量

public static void main(String[] args) throws Exception {
		Class clazz = Class.forName("com.heima.bean.Person");
		Constructor c = clazz.getConstructor(String.class, int.class);
		Person p = (Person) c.newInstance("张三", 23);
		// Field field = clazz.getField("name");
		Field field = clazz.getDeclaredField("name");
		field.setAccessible(true);
		field.set(p, "李四");
		System.out.println(p);
	}

clazz的getField可以获取成员变量字段,但是如果是私有的,那么就会报错:

Exception in thread "main" java.lang.NoSuchFieldException: name

getDeclaredField可以获取私有的,属于暴力反射,如果没有setAccessible,就会报错:

Exception in thread "main" java.lang.IllegalAccessException: Class com.heima.reflect.Demo4_Field can not access a member of class com.heima.bean.Person with modifiers "private"

field也可以通过set方法为属性赋值

5.通过反射获取方法及应用 

通过反射获取方法

public static void main(String[] args) throws Exception {
		Class clazz = Class.forName("com.heima.bean.Person");
		Constructor c = clazz.getConstructor(String.class, int.class);
		Person p = (Person) c.newInstance("张三", 17);
		Method method = clazz.getMethod("eat");
		method.invoke(p);
		Method method2 = clazz.getMethod("eat", int.class);
		method2.invoke(p, 8);
	}

get类方法都是宽泛的类获取,获取以后的操作才涉及到具体的对象操作

通过反射越过泛型检查

public static void main(String[] args) throws Exception {
		ArrayList<Integer> list = new ArrayList<>();
		list.add(6);
		list.add(8);
		System.out.println(list);
		Class clazz = Class.forName("java.util.ArrayList");
		Method method = clazz.getMethod("add", Object.class);
		method.invoke(list, "这是一个字符串");
		System.out.println(list);
	}

又名:泛型擦除,泛型反射,获取方法,传入参数

因为泛型在编译时期有效,运行时期会被擦除,所以可以通过泛型反射在反省中添加不同类型的数据,需要注意的是add中的参数为E,也就是Object

通过反射写一个通用的设置某个对象的某个属性为指定的值

public static void main(String[] args) throws Exception {
		Student s = new Student("张三", 19);
		System.out.println(s);
		Tool t = new Tool();
		t.setProperty(s, "name", "李四");
		System.out.println(s);
	}
public class Tool {
	// 此方法可将obj对象中名为propertyName的属性的值设置为value。
	public void setProperty(Object obj, String propertyName, Object value) throws Exception {
		Class clazz = obj.getClass();
		Field field = clazz.getDeclaredField(propertyName);
		field.setAccessible(true);
		field.set(obj, value);
	}
}

通过读取配置文件构建实例并调用方法

public static void main(String[] args) throws Exception {
		BufferedReader br = new BufferedReader(new FileReader("config1.properties"));
		Class clazz = Class.forName(br.readLine());
		DemoClass dc = (DemoClass) clazz.newInstance();
		dc.run();
		br.

6.动态代理

代理:自己做的事情,请别人来做

动态代理:动态代理就是通过反射生成代理。

在java中,java.lang.reflect包下提供了一个Proxy类和InvocationHandler接口,通过使用这个类和接口就可以生成动态代理对象。

使用Proxy类的

static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 
          返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。

完成动态代理,也必须使用InvocationHandler接口的

 Object invoke(Object proxy, Method method, Object[] args) 
          在代理实例上处理方法调用并返回结果。

案例代码:

public class MyInvocationHandler implements InvocationHandler {
	private Object target;
	
	public MyInvocationHandler(Object target) {
		this.target = target;
	}
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		System.out.println("权限校验");
		method.invoke(target, args);					//执行被代理target对象的方法
		System.out.println("日志记录");
		return null;
	}

}
public static void main(String[] args) {
		/*UserImp ui = new UserImp();
		ui.add();
		ui.delete();
		
		System.out.println("-------------------------------");*/
		/*
		 * public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,
		 * InvocationHandler h)
		 */
		/*MyInvocationHandler m = new MyInvocationHandler(ui);
		User u = (User)Proxy.newProxyInstance(ui.getClass().getClassLoader(), ui.getClass().getInterfaces(), m);
		u.add();
		u.delete();*/
		
		StudentImp si = new StudentImp();
		si.login();
		si.submit();
		
		System.out.println("-------------------------------");
		MyInvocationHandler m = new MyInvocationHandler(si);
		Student s = (Student)Proxy.newProxyInstance(si.getClass().getClassLoader(), si.getClass().getInterfaces(), m);
		
		s.login();
		s.submit();
	}

7.模板设计模式Template

模板方法设计模式就是定义一个算法的骨架,而将具体的算法实现延迟到子类中来实现。

优点:使用模板设计模式,在定义算法骨架的同时,可以灵活地根据需求修改具体算法

缺点:算法骨架改变需要修改具体的抽象类

案例代码:

public class Demo1_Temple {

	public static void main(String[] args) {
		Demo d = new Demo();
		long time = d.getTime();
		System.out.println(time);
	}

}

abstract class GetTime {
	public final long getTime() {
		long start = System.currentTimeMillis();
		code();
		long end = System.currentTimeMillis();
		return end - start;
	}

	public abstract void code();
}

class Demo extends GetTime {

	@Override
	public void code() {
		int i = 0;
		while (i < 100000) {
			System.out.println("x");
			i++;
		}

	}

}

定义一个抽象类,用final修饰整个框架的方法,框架内部具体的算法部分实现抽取成为抽象方法,定义子类实现具体的算法。功能实现的时候直接new子类对象,调用final框架方法即可。

8.JDK5新特性:枚举

枚举:将变量的值一一列出来,变量的值只限于列举出来的值的范围内,一年十二个月,一周七天等。

单例:一个类只能有一个实例。

多例类是有有限个实例。

自己实现枚举类三种方法:

public static void demo3() {
		Week3 week1 = Week3.week1;
		Week3 week2 = Week3.week2;
		Week3 week3 = Week3.week3;
	}

	public static void demo2() {
		Week2 week1 = Week2.week1;
		Week2 week2 = Week2.week2;
		Week2 week3 = Week2.week3;
	}

	public static void demo1() {
		Week week1 = Week.week1;
		Week week2 = Week.week2;
		Week week3 = Week.week3;
		week1.show();
		week2.show();
		week3.show();
	}
public class Week {
	public static final Week week1 = new Week();
	public static final Week week2 = new Week();
	public static final Week week3 = new Week();

	private Week() {

	}

	public void show() {
		System.out.println("---");
	}
}
public class Week2 {
	public static final Week2 week1 = new Week2("周一");
	public static final Week2 week2 = new Week2("周二");
	public static final Week2 week3 = new Week2("周三");
	private String name;

	private Week2(String name) {
		this.name = name;
		System.out.println(name);
	}

	public void show() {
		System.out.println("---");
	}
}
public abstract class Week3 {
	public static final Week3 week1 = new Week3("周一") {

		@Override
		public void show() {
			System.out.println("今天是周一");
		}

	};
	public static final Week3 week2 = new Week3("周二") {

		@Override
		public void show() {
			System.out.println("今天是周二");
		}

	};
	public static final Week3 week3 = new Week3("周三") {

		@Override
		public void show() {
			System.out.println("今天是周三");
		}

	};

	private Week3(String name) {
		System.out.println(name);
	}

	public abstract void show();
}

无参的,有参的,匿名内部类构建的。

enum类

上面三种自定义枚举的简化版本,可以直接在项目中创建Enum:

public enum Week {
	week1, week2, week3;
}
public enum Week2 {
	week1("星期一"), week2("星期二"), week3("星期三");
	private String name;

	private Week2(String name) {
		this.setName(name);
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}
public enum Week3 {
	week1 {
		@Override
		public void show() {
			System.out.println("星期一");
		}
	},
	week2 {
		@Override
		public void show() {
			System.out.println("星期二");
		}
	},
	week3 {
		@Override
		public void show() {
			System.out.println("星期三");
		}
	};
	public abstract void show();
}

枚举的注意事项

定义枚举类要用关键字enum

枚举类的第一行必须是枚举项

枚举类的枚举项后面如果有代码就不能省略;,没有代码可以省略;

所有枚举类都必须是Enum的子类,

枚举类也可以由抽象方法,但是枚举项必须匿名内部类重写该方法。

枚举类可以有构造器,但是一定是private修饰的,默认也是。

枚举在Switch中的使用

Week3 week1 = Week3.week1;
		switch (week1) {
			case week1:
				System.out.println("周一");
				break;
			case week2:
				System.out.println("周二");
				break;
			case week3:
				System.out.println("周三");
				break;
		}

枚举类的常用方法

案例代码:

public class Test2 {
	/**
	 * int ordinal()
	 * int compareTo(E o)
	 * String name()
	 * String toString()
	 * <T> T valueOf(Class<T> type,String name)
	 * values()
	 * 此方法虽然在JDK文档中查找不到,但每个枚举类都具有该方法,它遍历枚举类的所有枚举值非常方便
	 */
	public static void main(String[] args) {
		Week2 week1 = Week2.week1;
		Week2 week2 = Week2.week2;
		Week2 week3 = Week2.week3;
		System.out.println(week1.ordinal());
		System.out.println(week2.ordinal());
		System.out.println(week1.compareTo(week2));
		System.out.println(week2.compareTo(week3));
		System.out.println(week1.name());
		System.out.println("----------");
		System.out.println(week1.toString());
		Week2 valueOf = Week2.valueOf(Week2.class, "week1");
		System.out.println(valueOf.toString());
		Week2 valueOf2 = Week2.valueOf("week1");
		System.out.println(valueOf2.toString());
		System.out.println("-----------");
		Week2[] values = Week2.values();
		for (Week2 week22 : values) {
			System.out.println(week22);
		}
	}

}

9.JDK7的新特性:

               A:二进制字面量

               B:数字字面量可以出现下划线

               C:switch 语句可以用字符串

               D:泛型简化,菱形泛型

               E:异常的多个catch合并,每个异常用或|

               F:try-with-resources 语句,1.7版标准的异常处理代码

10.JDK8的新特性:

接口中可以定义有方法体的方法,如果是非静态,要用default修饰,如果是静态,就不用修饰。

局部内部类使用局部变量在JDK8中不需要加final修饰

为什么以前需要final修饰呢?

调用方法时候,如果没有final修饰,局部变量的生命周期和方法是一样的,方法使用结束会弹栈,局部变量就随之消失,内部类没有马上消失,想要调用局部变量就会失败。而有final修饰,就进入常量池中,即使方法弹栈,不会发生类似情况。

猜你喜欢

转载自blog.csdn.net/YooFale/article/details/83075127