27.反射,类加载器,设计模式,jdk新特性

时间:2022-05-04
本文章向大家介绍27.反射,类加载器,设计模式,jdk新特性,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
1:反射(理解)

(1)类的加载及类加载器

类的加载:

当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。

加载

就是指将class文件读入内存,并为之创建一个Class对象。

任何类被使用时系统都会建立一个Class对象。

连接

验证 是否有正确的内部结构,并和其他类协调一致

准备 负责为类的静态成员分配内存,并设置默认初始化值

解析 将类的二进制数据中的符号引用替换为直接引用

初始化 就是我们以前讲过的初始化步骤

类初始化时机:

创建类的实例

访问类的静态变量,或者为静态变量赋值

调用类的静态方法

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

初始化某个类的子类

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

类加载器:

负责将.class文件加载到内在中,并为之生成对应的Class对象。

虽然我们不需要关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行。

类加载器的组成

Bootstrap ClassLoader 根类加载器

Extension ClassLoader 扩展类加载器

Sysetm ClassLoader 系统类加载器

类加载器作用:

Bootstrap ClassLoader 根类加载器

也被称为引导类加载器,负责Java核心类的加载

比如System,String等。在JDK中JRE的lib目录下rt.jar文件中

Extension ClassLoader 扩展类加载器

负责JRE的扩展目录中jar包的加载。

在JDK中JRE的lib目录下ext目录

Sysetm ClassLoader 系统类加载器

负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径

(2)反射:

通过字节码文件对象,去使用成员变量,构造方法,成员方法

package cn.itcast_01;
public class Person {
 private String name;
 int age;
 public String address;
 public Person() {
 }
 private Person(String name) {
 this.name = name;
 }
 Person(String name, int age) {
 this.name = name;
 this.age = age;
 }
 public Person(String name, int age, String address) {
 this.name = name;
 this.age = age;
 this.address = address;
 }
 public void show() {
 System.out.println("show");
 }
 public void method(String s) {
 System.out.println("method " + s);
 }
 public String getString(String s, int i) {
 return s + "---" + i;
 }
 private void function() {
 System.out.println("function");
 }
 @Override
 public String toString() {
 return "Person [name=" + name + ", age=" + age + ", address=" + address
 + "]";
 }
}
package cn.itcast_01;
/*
 * 反射:就是通过class文件对象,去使用该文件中的成员变量,构造方法,成员方法。
 * 
 * Person p = new Person();
 * p.使用
 * 
 * 要想这样使用,首先你必须得到class文件对象,其实也就是得到Class类的对象。
 * Class类:
 *  成员变量 Field
 *  构造方法 Constructor
 *  成员方法 Method
 * 
 * 获取class文件对象的方式:
 * A:Object类的getClass()方法
 * B:数据类型的静态属性class
 * C:Class类中的静态方法
 *  public static Class forName(String className)
 * 
 * 一般我们到底使用谁呢?
 *  A:自己玩 任选一种,第二种比较方便
 *  B:开发 第三种
 *  为什么呢?因为第三种是一个字符串,而不是一个具体的类名。这样我们就可以把这样的字符串配置到配置文件中。
 */
public class ReflectDemo {
 public static void main(String[] args) throws ClassNotFoundException {
 // 方式1
 Person p = new Person();
 Class c = p.getClass();
 Person p2 = new Person();
 Class c2 = p2.getClass();
 System.out.println(p == p2);// false
 System.out.println(c == c2);// true
 // 方式2
 Class c3 = Person.class;
 // int.class;
 // String.class;
 System.out.println(c == c3);
 // 方式3
 // ClassNotFoundException
 Class c4 = Class.forName("cn.itcast_01.Person");
 System.out.println(c == c4);
 }
}
 (3)反射的使用
 A:通过反射获取构造方法并使用
package cn.itcast_02;(1)
import java.lang.reflect.Constructor;
import cn.itcast_01.Person;
/*
 * 通过反射获取构造方法并使用。
 */
public class ReflectDemo {
 public static void main(String[] args) throws Exception {
 // 获取字节码文件对象
 Class c = Class.forName("cn.itcast_01.Person");
 // 获取构造方法
 // public Constructor[] getConstructors():所有公共构造方法
 // public Constructor[] getDeclaredConstructors():所有构造方法
 // Constructor[] cons = c.getDeclaredConstructors();
 // for (Constructor con : cons) {
 // System.out.println(con);
 // }
 // 获取单个构造方法
 // public Constructor<T> getConstructor(Class<?>... parameterTypes)
 // 参数表示的是:你要获取的构造方法的构造参数个数及数据类型的class字节码文件对象
 Constructor con = c.getConstructor();// 返回的是构造方法对象
 // Person p = new Person();
 // System.out.println(p);
 // public T newInstance(Object... initargs)
 // 使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。
 Object obj = con.newInstance();
 System.out.println(obj);
 // Person p = (Person)obj;
 // p.show();
 }
}
package cn.itcast_02;(2)
import java.lang.reflect.Constructor;
/*
 * 需求:通过反射去获取该构造方法并使用:
 * public Person(String name, int age, String address)
 * 
 * Person p = new Person("林青霞",27,"北京");
 * System.out.println(p);
 */
public class ReflectDemo2 {
 public static void main(String[] args) throws Exception {
 // 获取字节码文件对象
 Class c = Class.forName("cn.itcast_01.Person");
 // 获取带参构造方法对象
 // public Constructor<T> getConstructor(Class<?>... parameterTypes)
 Constructor con = c.getConstructor(String.class, int.class,
 String.class);
 // 通过带参构造方法对象创建对象
 // public T newInstance(Object... initargs)
 Object obj = con.newInstance("林青霞", 27, "北京");
 System.out.println(obj);
 }
}
package cn.itcast_02;(3)
import java.lang.reflect.Constructor;
/*
 * 需求:通过反射获取私有构造方法并使用
 * private Person(String name){}
 * 
 * Person p = new Person("风清扬");
 * System.out.println(p);
 */
public class ReflectDemo3 {
 public static void main(String[] args) throws Exception {
 // 获取字节码文件对象
 Class c = Class.forName("cn.itcast_01.Person");
 // 获取私有构造方法对象
 // NoSuchMethodException:每个这个方法异常
 // 原因是一开始我们使用的方法只能获取公共的,下面这种方式就可以了。
 Constructor con = c.getDeclaredConstructor(String.class);
 // 用该私有构造方法创建对象
 // IllegalAccessException:非法的访问异常。
 // 暴力访问
 con.setAccessible(true);// 值为true则指示反射的对象在使用时应该取消Java语言访问检查。
 Object obj = con.newInstance("风清扬");
 System.out.println(obj);
 }
}
 B:通过反射获取成员变量并使用
package cn.itcast_03;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
/*
 * 通过发生获取成员变量并使用
 */
public class ReflectDemo {
 public static void main(String[] args) throws Exception {
 // 获取字节码文件对象
 Class c = Class.forName("cn.itcast_01.Person");
 // 获取所有的成员变量
 // Field[] fields = c.getFields();
 // Field[] fields = c.getDeclaredFields();
 // for (Field field : fields) {
 // System.out.println(field);
 // }
 /*
  * Person p = new Person(); p.address = "北京"; System.out.println(p);
  */
 // 通过无参构造方法创建对象
 Constructor con = c.getConstructor();
 Object obj = con.newInstance();
 System.out.println(obj);
 // 获取单个的成员变量
 // 获取address并对其赋值
 Field addressField = c.getField("address");
 // public void set(Object obj,Object value)
 // 将指定对象变量上此 Field 对象表示的字段设置为指定的新值。
 addressField.set(obj, "北京"); // 给obj对象的addressField字段设置值为"北京"
 System.out.println(obj);
 // 获取name并对其赋值
 // NoSuchFieldException
 Field nameField = c.getDeclaredField("name");
 // IllegalAccessException
 nameField.setAccessible(true);
 nameField.set(obj, "林青霞");
 System.out.println(obj);
 // 获取age并对其赋值
 Field ageField = c.getDeclaredField("age");
 ageField.setAccessible(true);
 ageField.set(obj, 27);
 System.out.println(obj);
 }
}
 C:通过反射获取成员方法并使用
package cn.itcast_04;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class ReflectDemo {
 public static void main(String[] args) throws Exception {
 // 获取字节码文件对象
 Class c = Class.forName("cn.itcast_01.Person");
 // 获取所有的方法
 // Method[] methods = c.getMethods(); // 获取自己的包括父亲的公共方法
 // Method[] methods = c.getDeclaredMethods(); // 获取自己的所有的方法
 // for (Method method : methods) {
 // System.out.println(method);
 // }
 Constructor con = c.getConstructor();
 Object obj = con.newInstance();
 /*
  * Person p = new Person(); p.show();
  */
 // 获取单个方法并使用
 // public void show()
 // public Method getMethod(String name,Class<?>... parameterTypes)
 // 第一个参数表示的方法名,第二个参数表示的是方法的参数的class类型
 Method m1 = c.getMethod("show");
 // obj.m1(); // 错误
 // public Object invoke(Object obj,Object... args)
 // 返回值是Object接收,第一个参数表示对象是谁,第二参数表示调用该方法的实际参数
 m1.invoke(obj); // 调用obj对象的m1方法
 System.out.println("----------");
 // public void method(String s)
 Method m2 = c.getMethod("method", String.class);
 m2.invoke(obj, "hello");
 System.out.println("----------");
 // public String getString(String s, int i)
 Method m3 = c.getMethod("getString", String.class, int.class);
 Object objString = m3.invoke(obj, "hello", 100);
 System.out.println(objString);
 // String s = (String)m3.invoke(obj, "hello",100);
 // System.out.println(s);
 System.out.println("----------");
 // private void function()
 Method m4 = c.getDeclaredMethod("function");
 m4.setAccessible(true);
 m4.invoke(obj);
 }
}

(4)反射案例

A:通过反射运行配置文件的内容

B:通过反射越过泛型检查

C:通过反射给任意的一个对象的任意的属性赋值为指定的值

(5)动态代理

动态代理:

代理:本来应该自己做的事情,却请了别人来做,被请的人就是代理对象。

举例:春季回家买票让人代买

动态代理:在程序运行过程中产生的这个对象

而程序运行过程中产生对象其实就是我们刚才反射讲解的内容,所以,动态代理其实就是通过反射来生成一个代理

在Java中java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过使用这个类和接口就可以生成动态代理对象。JDK提供的代理只能针对接口做代理。我们有更强大的代理cglib

Proxy类中的方法创建动态代理类对象

public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)

最终会调用InvocationHandler的方法

InvocationHandler

Object invoke(Object proxy,Method method,Object[] args)

2:设计模式

(1)装饰设计模式

装饰设计模式概述

装饰模式就是使用被装饰类的一个子类的实例,在客户端将这个子类的实例交给装饰类。是继承的替代方案

优点

使用装饰模式,可以提供比继承更灵活的扩展对象的功能,它可以动态的添加对象的功能,并且可以随意的组合这些功能

缺点

正因为可以随意组合,所以就可能出现一些不合理的逻辑

 BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
 Scanner sc = new Scanner(System.in);
package cn.itcast_02;
public interface Phone {
 public abstract void call();
}
package cn.itcast_02;
public class IPhone implements Phone {
 @Override
 public void call() {
 System.out.println("手机可以打电话了");
 }
}
package cn.itcast_02;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.Scanner;
public class PhoneDemo {
 public static void main(String[] args) {
 Phone p = new IPhone();
 p.call();
 System.out.println("------------");
 // 需求:我想在接电话前,听彩铃
 PhoneDecorate pd = new RingPhoneDecorate(p);
 pd.call();
 System.out.println("------------");
 // 需求:我想在接电话后,听音乐
 pd = new MusicPhoneDecorate(p);
 pd.call();
 System.out.println("------------");
 // 需求:我要想手机在接前听彩铃,接后听音乐
 // 自己提供装饰类,在打电话前听彩铃,打电话后听音乐
 pd = new RingPhoneDecorate(new MusicPhoneDecorate(p));
 pd.call();
 System.out.println("----------");
 // 想想我们在IO流中的使用
 // InputStream is = System.in;
 // InputStreamReader isr = new InputStreamReader(is);
 // BufferedReader br = new BufferedReader(isr);
 BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
 BufferedWriter bw = new BufferedWriter((new OutputStreamWriter(
 System.out)));
 Scanner sc = new Scanner(System.in);
 }
}
package cn.itcast_02;
public abstract class PhoneDecorate implements Phone {
 private Phone p;
 public PhoneDecorate(Phone p) {
 this.p = p;
 }
 @Override
 public void call() {
 this.p.call();
 }
}
package cn.itcast_02;
public class RingPhoneDecorate extends PhoneDecorate {
 public RingPhoneDecorate(Phone p) {
 super(p);
 }
 @Override
 public void call() {
 System.out.println("手机可以听彩铃");
 super.call();
 }
}
package cn.itcast_02;
public class MusicPhoneDecorate extends PhoneDecorate {
 public MusicPhoneDecorate(Phone p) {
 super(p);
 }
 @Override
 public void call() {
 super.call();
 System.out.println("手机可以听音乐");
 }
}

(2)模版设计模式

模版设计模式概述

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

优点

使用模版方法模式,在定义算法骨架的同时,可以很灵活的实现具体的算法,满足用户灵活多变的需求

缺点

如果算法骨架有修改的话,则需要修改抽象类

 package cn.itcast_01;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public abstract class GetTime {
 // 需求:请给我计算出一段代码的运行时间
 public long getTime() {
 long start = System.currentTimeMillis();
 // for循环
 // for (int x = 0; x < 10000; x++) {
 // System.out.println(x);
 // }
 // 视频
 // try {
 // BufferedInputStream bis = new BufferedInputStream(
 // new FileInputStream("a.avi"));
 // BufferedOutputStream bos = new BufferedOutputStream(
 // new FileOutputStream("b.avi"));
 // byte[] bys = new byte[1024];
 // int len = 0;
 // while ((len = bis.read(bys)) != -1) {
 // bos.write(bys, 0, len);
 // }
 // bos.close();
 // bis.close();
 // } catch (IOException e) {
 // e.printStackTrace();
 // }
 // 再给我测试一个代码:集合操作的,多线程操作,常用API操作的等等...
 code();
 long end = System.currentTimeMillis();
 return end - start;
 }
 public abstract void code();
}
package cn.itcast_01;
public class GetTimeDemo {
 public static void main(String[] args) {
 // GetTime gt = new GetTime();
 // System.out.println(gt.getTime() + "毫秒");
 GetTime gt = new ForDemo();
 System.out.println(gt.getTime() + "毫秒");
 gt = new IODemo();
 System.out.println(gt.getTime() + "毫秒");
 }
}
package cn.itcast_01;
public class ForDemo extends GetTime {
 @Override
 public void code() {
 for (int x = 0; x < 100000; x++) {
 System.out.println(x);
 }
 }
}
package cn.itcast_01;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class IODemo extends GetTime{
 @Override
 public void code() {
 try {
 BufferedInputStream bis = new BufferedInputStream(
 new FileInputStream("a.avi"));
 BufferedOutputStream bos = new BufferedOutputStream(
 new FileOutputStream("b.avi"));
 byte[] bys = new byte[1024];
 int len = 0;
 while ((len = bis.read(bys)) != -1) {
 bos.write(bys, 0, len);
 }
 bos.close();
 bis.close();
 } catch (IOException e) {
 e.printStackTrace();
 }
 }
}

3:JDK新特性

(1)JDK5(掌握)

装箱和拆箱

泛型

增强for

静态导入

可变参数

枚举

枚举概述

是指将变量的值一一列出来,变量的值只限于列举出来的值的范围内。举例:一周只有7天,一年只有12个月等。

回想单例设计模式:单例类是一个类只有一个实例

那么多例类就是一个类有多个实例,但不是无限个数的实例,而是有限个数的实例。这才能是枚举类。

通过自己定义一个枚举类来演示案例

第一版

第二版

第三版

发现自己定义一个枚举类,比较麻烦,所以,java就提供了枚举类供我们使用。

格式是:只有枚举项的枚举类

public enum 枚举类名 {

枚举项1,枚举项2,枚举项3…;

}

int compareTo(E o)

String name()

int ordinal()

String toString()

<T> T valueOf(Class<T> type,String name)

values()

此方法虽然在JDK文档中查找不到,但每个枚举类都具有该方法,它遍历枚举类的所有枚举值非常方便

(2)JDK6(了解)

(3)JDK7(理解)

(1)二进制的表现形式:

JDK7开始,终于可以用二进制来表示整数(byte,short,int和long)。使用二进制字面量的好处是,可以使代码更容易被理解。语法非常简单,只要在二进制数值前面加 0b或者0B

举例:

int x = ob110110

(2)用_分隔数据:

为了增强对数值的阅读性,如我们经常把数据用逗号分隔一样。JDK7提供了_对数据分隔。

举例:

int x = 100_1000;

注意事项:

不能出现在进制标识和数值之间

不能出现在数值开头和结尾

不能出现在小数点旁边

(3)switch语句可是用字符串

switch 语句可以用字符串

(4)泛型推断(菱形泛型)

(5)多catch的使用

异常的多个catch合并

格式:

try(必须是java.lang.AutoCloseable的子类对象){…}

好处:

资源自动释放,不需要close()了

把需要关闭资源的部分都定义在这里就ok了

主要是流体系的对象是这个接口的子类(看JDK7的API)

(6)自动释放资源的用法

(4)JDK8(了解)

可以去网上了解资料