[TOC]

反射机制

1.类加载概述

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

  • (1)加载:是指将class文件读入到内存,并为之创建一个Class对象,任何类被使用的时候系统都会创建一个Class对象
  • (2)连接:
    • 验证:是否有正确的内部结构并和其他类协调一致;
    • 准备:负责为类的静态成员分配内存,并设置默认初始值;
    • 解析:将类的二进制数据中的符号引用替换成为直接引用
  • (3)初始化:就是我们以前讲过的初始化步骤

类加载时机流程:

  1. 创建类的实例;
  2. 访问类的静态变量或者为静态变量赋值;
  3. 使用类的静态方法
  4. 使用反射方式来强制创建某个类或者接口对应的java.lang.class对象;
  5. 初始化某个类的子类
  6. 直接使用java.exe命令来运行某个主类


2.类加载器概述

类加载器概述:负责将.class文件加载到内存中,并为之生成对应的class文件,虽然我们不需要关心类的加载机制,但是了解这个机制我们就能更好的理解程序的运行;
类加载器的分类:

  • BootStrp ClassLoader 根类加载器
  • Extension ClassLoader 扩展类加载器
  • System Classloader 系统类加载器

类的加载器的作用:

  1. Bootstrap ClassLoader 根类加载器: 也被称为引导类加载器,负责Java核心类的加载;比如System/String类等在JDK的JRE里lib目录下rt.jar文件中;
  2. Extension ClassLoader 扩展类加载器: 负责JRE的扩展目录中的jar包的加载,在JDK中JRE的lib目录下的ext目录;
  3. System ClassLoader 系统类加载器: 负责在JVM启动时候加载来自Java命令的class文件,以及classpath环境变量所指定的jar包和类路径;


3.类的反射

描述:Java反射机制是运行状态中对任意一个类,都能知道这个类的所有属性和方法;对于任意一个对象都能够调用它的任意一个方法和属性,这种动态获取的信息以及动态调用对象的方法的功能称为JAVA语言的反射机制,要想剖析一个类就必须先要获取到该类的字节码文件对象,而解剖使用的就是Class类中的方法,所以要先获取每个字节码文件对应的class类型的对象;

总结:反射Reflect一切都是赤裸裸的,包括私有的成员变量;

Class类方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//类的实例代表一个运行 类 java应用程序的类和接口。
public final class Class<T>
extends Object
implements Serializable, GenericDeclaration, Type, AnnotatedElement

//常见方法:
T newInstance() //创建这个 类对象表示的类的一个新实例。 注意这是增对于构造无参参数;
Constructor<T> getConstructor(类<?>... parameterTypes) //返回一个 Constructor对象反映指定的公共构造函数的 类对象表示的类。 构造有参函数
Constructor<?>[] getConstructors() //返回一个数组包含 Constructor物体反射所有的 类对象表示的类的公共构造函数。

Field getField(String name) //返回一个 Field对象反映的类或接口的 类对象表示的指定公共成员。
Field[] getFields() //返回一个数组包含 Field物体反射的类或接口的 类对象代表所有可访问的公共领域。
Field[] getDeclaredFields() //返回 Field物体反射所有字段的类或接口的 类对象表示声明数组。 包括私有的成员变量
Method getMethod(String name, 类<?>... parameterTypes) //返回一个 方法对象反映的类或接口的 类对象表示的指定公共成员方法。
Method[] getMethods() //返回一个数组包含 方法物体反射所有的类或接口的 类对象表示的公共方法,包括那些由类或接口的超类和超接口继承的声明。


补充类说明:

1
2
3
4
5
//Constructor提供有关信息和访问,一类一个构造函数。
public final class Constructor<T> extends Executable

//常用方法
T newInstance(Object... initargs) //利用这 Constructor对象创建和初始化的构造函数的声明类的一个新实例构造函数,用指定的初始化参数。


1
2
3
4
5
6
7
8
//一个 Field提供有关信息和动态访问,一个类或接口的一个单一的领域。反射场可以是一个类(静态)字段或一个实例字段。 
public final class Field
extends AccessibleObject
implements Member

//方法
void set(Object obj, Object value) //设置域为代表的这 Field对象指定对象上的参数指定的新价值。
public void setAccessible(boolean flag) throws SecurityException //该对象到指定的布尔值设置 accessible旗。一个价值 true表明反射的对象应当压制java语言访问检查时可以将私有成员变量变成共有;


1
2
3
4
//一个方法提供有关信息和访问,在类或接口的一个方法
public final class Method extends Executable
String getName() //返回的方法对象表示的方法的名称,作为一个 String
Object invoke(Object obj, Object... args) //调用底层的方法,这 方法对象表示,对指定对象的指定参数。 执行指定字节码中的方法


三种方式:

  • (1)Class类中静态方法forName读取配置文件
  • (2)静态属性class中锁对象
  • (3)Object 类的getClass()发布方法判断两个对象是否是同一个字解码文件
WeiyiGeek.

WeiyiGeek.

基础示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
/**
* 榨汁机Juicer示例:采用反射Class.forName()配置文件读取
* Fruit 水果接口
* Apple 、 Orange 、 Banana
* squeeze 榨汁
*/
package com.weiyigeek.reflect;
import java.io.BufferedReader;
import java.io.FileReader;
public class Demo2_ReflectForName {
public static void main(String[] args) throws Exception {
Juicer j = new Juicer(); //相当于购买榨汁机
//采用反射进行配置文件读取config.properties
BufferedReader br = new BufferedReader(new FileReader("config.properties")); //配置文件中读取class对象

//苹果 /橘子 (非常值得学习)
String line;
while((line = br.readLine()) != null) {
Class clazz2 = Class.forName(line); //读取一行 => 包.类名 要使用其他的时候直接更改名称即可
Fruit o= (Fruit) clazz2.newInstance(); //向上转型 父类引用指向子类对象(创建一个新的对象),水果的引用指向评估 j.run(f);
j.run(o);
}
}
}

//水果接口
interface Fruit {
public void squeeze();
}
//苹果汁
class Apple implements Fruit {
@Override
public void squeeze() {
System.out.println("这是一杯 苹果 汁");
}
}
//橘子汁
class Orange implements Fruit {
@Override
public void squeeze() {
System.out.println("这是一杯 橘子 汁");
}
}
//香蕉汁
class Banana implements Fruit {
@Override
public void squeeze() {
System.out.println("这是一杯 香蕉 汁");
}
}

//榨汁机类
class Juicer {
public void run(Fruit a){
a.squeeze();
}
}

config.properties
1
2
com.weiyigeek.reflect.Orange
com.weiyigeek.reflect.Apple


基础示例:

  • 1.创建对象的三种方式
  • 2.利用字节码中无参构造和有参构造创建对象
    • 2.1 class 类的newInstance()方法是使用该类无参的构造函数创建对象
    • 2.2 调用Class类中的getConstructor(String.class,int.class)方法获取一个指定的构造函数然后再调用Constructor类的newInstance先有参函数创建对象
  • 3.通过反射获取成员变量并使用分为共有public和私有private
  • 4.通过反射获取成员方法并使用分为 无参方法 和 有参方法
  • 5.通过反射越过泛型的监测,泛型特性:只在编译期间有效,在运行期会被擦除掉;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package com.weiyigeek.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import com.weiyi.Collection.Students;
public class Demo1_Reflect {
public static void main(String[] args) throws Exception {
//1.获取Class文件对象的三种红方式
Class clazz1 = Class.forName("com.weiyi.Collection.Students"); //方式1 获取一个类的字节码文件
Class clazz2 = Students.class; //方式2

Students clazz = new Students();
Class clazz3 = clazz.getClass(); //方式3

//判断获取的三种方式的class对象是否相同(都是想相同的只不过处于三种不同的阶段)
System.out.println(clazz1 == clazz2); //true
System.out.println(clazz3 == clazz2); //true


//2.反射获取带参数的构造方法并使用
//Class clazz4 = Class.forName("com.weiyigeek.Collection.Students"); //[Error]重复加载反射类会报错
Constructor<Students> cs = clazz1.getConstructor(String.class,int.class); //获取有参构造
Students s = (Students) cs.newInstance("有参构造",1024); //重点。利用有参构造创建对象
System.out.println(s);


//3.通过反射获取成员变量并使用
//公共成员变量的时候
//Field demo = clazz1.getField("name");
//demo.set(s, "王三");

//私有成员变量的时候
Field f = clazz1.getDeclaredField("name"); //暴力获取字段name
f.setAccessible(true); //去除私有属性
f.set(s, "Field");
System.out.println(s);


//4.通过反射获取方法
Method m = clazz1.getMethod("reflectDemo"); //获取类中的方法
m.invoke(s); //执行无参方法reflectDemo
Method m1 = clazz1.getMethod("reflectDemo", int.class); //获取有参数方式
m1.invoke(s, 1024);


//5.通过反射越过泛型检测
//ArrayList <Integer>的一个对象,在这个集合中添加一个字符串数据如何实现的呢?
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
//注意这里由于泛型的特性不能加入字符串 "abc"
//list.add("abc")
//但是我们可以通过反射Reflect特性来添加
Class clazz6 = Class.forName("java.util.ArrayList"); //获取ArrayList字节码对象
Method m2 = clazz6.getMethod("add",Object.class); //获取add方法
m2.invoke(list, "abc");
System.out.println("反射绕过泛型: " + list);
}
}

执行结果:

1
2
3
4
5
6
7
true
true
Students [name=有参构造, age=1024]
Students [name=Field, age=1024]
反射获取的无参方法!
反射获取的有参方法,a = 1024
反射绕过泛型: [1, 2, abc]


综合实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
//测试反射类
package com.weiyigeek.reflect;
import java.lang.reflect.Field;
public class Demo3_ReflectTool {
//1.通过反射写一个通用类来进行修改某个属性值(实际把前面所学做一个总结)
public Demo3_ReflectTool() {};
public void setProperty(Object obj, String propertyName, Object value) throws Exception {
Class clazz = obj.getClass(); //获取字节码对象
Field f = clazz.getDeclaredField(propertyName); //或者对象中私有成员变量
f.setAccessible(true); //去除私有成员变量权限
f.set(obj, value); //设置改成员变量得值
}
public void run() {
System.out.println("Welcome to China");
}
}


//调用类
package com.weiyigeek.reflect;
import java.io.BufferedReader;
import java.io.FileReader;
import com.weiyi.Collection.Students;
public class Demo4_UseReflect {
public static void main(String[] args) throws Exception {
//1.实例1Reflect通用属性
Students stu = new Students("张三",1024);
System.out.println("修改前:" + stu);

Demo3_ReflectTool tool = new Demo3_ReflectTool();
tool.setProperty(stu,"name","王五");
System.out.println("修改后:" + stu);

//2.反射练习
//写一个Properties格式得配置文件,配置类得完整名称;
//写一个程序读该配置文件获得类得完整名称并几种这个类,用反射得方法运行run
BufferedReader br = new BufferedReader(new FileReader("config.properties")); //创建输入流
Class clazz = Class.forName(br.readLine()); //读取配置文件中类名获取字节码对象
Demo3_ReflectTool dr = (Demo3_ReflectTool) clazz.newInstance(); //通过字节码创建该对象 (需要强转)
dr.run(); //即可调用Demo3_UseReflect类中得方法
}
}

执行结果:
1
2
3
修改前:Students [name=张三, age=1024]
修改后:Students [name=王五, age=1024]
Welcome to China


补充知识:

  • 框架相当是一个房子,反射就相当于对房子不断的修改装修;


4.动态代理

动态代理概述:

  • 代理:本类应该自己做得事情,请了别人来做,被请的人就是代理对象;
  • 举例:春节回家买票让人代买,强最新版本的Iphone手机
  • 动态代理:在程序运行过程中产生的这个对象,而程序运行过程中产生对象其实就是我们刚才反射讲解的内容,所以动态代理其实就是通过反射来生成的一个代理;

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//注意:反射的包
java.lang.Object
java.lang.reflect.Proxy

//类申明
public class Proxy
extends Object
implements Serializable

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

//最终会调用InvocationHandker的方法 重写该方法即可;
//进行代理,执行某方法
InvocationHandler Object invoke(Object proxy, Method method, Object[] args)

实际案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
//  Demo5_User 接口类
package com.weiyigeek.reflect;
interface Demo5_User {
public void login();
public void run();
}

//Demo5_UserImp 实现接口
package com.weiyigeek.reflect;
public class Demo5_UserImp implements Demo5_User {
@Override
public void login() {
System.out.println("- 1.登录操作");
}

@Override
public void run() {
System.out.println("- 2.运行操作");
}
}


// Demo5_MyInvocationHandler 动态调度处理器
package com.weiyigeek.reflect;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class Demo5_MyInvocationHandler implements InvocationHandler {
//动态代理类
private Object target;

public Demo5_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;
}
}

// Demo5_Test.java #动态代理的测试
package com.weiyigeek.reflect;
import java.lang.reflect.Proxy;
public class Demo5_Test {
public static void main(String[] args) {
Demo5_UserImp ui = new Demo5_UserImp();
//我创建的动态代理类
Demo5_MyInvocationHandler dm = new Demo5_MyInvocationHandler(ui); //传入对象

//首先拿到字节码文件 -> 类加载器 ,再拿到该对象的接口
Demo5_User du = (Demo5_User)Proxy.newProxyInstance(ui.getClass().getClassLoader(), ui.getClass().getInterfaces(), dm);
du.login(); //运行类中方法,并加入了动态代理中添加的方法;
du.run(); //运行字节码对象中得方法
}
}

执行结果:
1
2
3
4
5
6
7
#就可以对任意得类中方法进行动态代理,添加功能等等
权限校验
- 1.登录操作
日志记录
权限校验
- 2.运行操作
日志记录


模板设计模式

模板template设计模式概述:模板方法模式就是一个定义一个算法得骨架,将具体得算法延迟到子类中来实现;

  • 优点:使用模板方式模式,再定义算法骨架得同时,可以很灵活得实现具体得算法,满足用户灵活多变得需求:
  • 缺点: 如果算法骨架有修改得画,则需要修改抽象类;

类设计模式总结:

1
2
3
4
5
6
1.装饰设计:装饰接口实现的类再次进行封装;
2.单例:单例类是一个类只有一个实例;
3.简单工厂模式:
4.工厂方法
5.适配器
6.模板

模板设计案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//模板方法设计模式:
package com.weiyigeek.reflect;
public class Demo6_Template {
public static void main(String[] args) {
Temp tmp = new Temp();
System.out.println("花费时间为: " + tmp.getRunTime());
}
}

//建立一个抽象类
abstract class GetRunTime {
//拥有抽象方法(abstract)本身必须是抽象类,为了防止类方法被重新加上一个final进行修饰,子类可以直接调用父类方法;
public final long getRunTime() {
long start = System.currentTimeMillis();
code();
long end = System.currentTimeMillis();
return end - start;
}
public abstract void code();
}


//创建一个类并继承该抽象类
class Temp extends GetRunTime {
@Override
public void code() {
for(int i = 0; i < 10000; i++)
System.out.println(i);
}
}

运行结果:
1
2
9999
花费时间为: 102


枚举类

描述:枚举是指将变量得值一一列举出来,变量得值只限于列举处理得值得范围内;
例如: 与C语言中枚举是一样,一个变量只能是再枚举范围内得值,比如一周只有7天,一年只有12个月等等;

与单例设计模式,那么多例类就是一个类有多个实例,但是部署无限个数得实例。而是有限个数得实例,这才能是枚举类。

关键技术:

1
2
3
4
5
6
7
1.自动拆装箱
2.泛型
3.可变参数
4.静态导入
5.增强for循环
6.互斥锁
7.枚举

枚举类常见方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//包:
java.lang.Object
java.lang.Enum<E>

//类:
public abstract class Enum<E extends Enum<E>>
extends Object
implements Comparable<E>, Serializable

//常规方法
int ordinal() //返回此枚举常量的顺序(位置在枚举声明,在初始常数是零分序号)。
int compareTo(E o) //这个枚举与指定对象按顺序比较。
int hashCode() //返回此枚举常量的哈希代码。
String name() //返回此枚举常量的名称,如宣布在其枚举声明。枚举项
String toString() //返回此枚举常量的名称,包含在声明。
static <T extends Enum<T>>T valueOf(class<T> enumType, String name) //通过字节码码对象获取枚举项;
Values() //在JDK文档中查询不到,但是每个枚举类都具有该方法,它遍历枚举类的所有枚举值非常方便,一般是重写tostring;

注意事项:

  • 定义枚举类要用关键字enum
  • 所有的枚举类都是Enum的子类
  • 枚举类的第一行上必须是枚举项,最后一个枚举项后的分号是可以省略的,但是如果枚举类有其他东西就不能省略 “;”
  • 枚举类有构造器但必须是private其实默认修饰符也是它
  • 枚举类也可以有抽象的方法(默认都是抽象类),但是枚举项必须重写该方法
  • 枚举在Switch语句中的使用


实例1:自己实现枚举

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
//(1)自定义枚举Enumerate 类
package com.weiyigeek.reflect;
public class Demo7_Week {
//方法1:无参
public static final Demo7_Week Mon = new Demo7_Week();
public static final Demo7_Week Tue = new Demo7_Week();
public static final Demo7_Week Wen = new Demo7_Week();
private Demo7_Week() {}; //私有构造不让其他类创建本类对象

//方法2:有参
public static final Demo7_Week Thu = new Demo7_Week("星期四");
public static final Demo7_Week Fri= new Demo7_Week("星期五");
public static final Demo7_Week Sat= new Demo7_Week("星期六");
private String week;
private Demo7_Week(String w) {
this.week = w; //私有构造不让其他类创建本类对象
}
public String getWeek() {
return week;
}
}


//(2)自定义枚举Enumerate类
//方法3: 枚举得第三种形式比较难
package com.weiyigeek.reflect;
//由于是抽象类所以采用内部匿名类
public abstract class Demo7_Week1 {
//父类引用指向子类对象 (创建该类得子类对象)
public static final Demo7_Week1 Sun = new Demo7_Week1("星期天") {
public void show() {
System.out.println("星期天");
}
};
private String week;
public Demo7_Week1(String w) {
this.week = w;
}
//我们说过抽象方法必须在抽象类中
public abstract void show();
}

Demo7_Enumerate.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.weiyigeek.reflect;
public class Demo7_Enumerate {
public static void main(String[] args) {
//实例1.枚举得使用
Demo7_Week mon = Demo7_Week.Mon;
Demo7_Week tue = Demo7_Week.Tue;
Demo7_Week wed = Demo7_Week.Wen;
System.out.println("方式1:");
System.out.println(mon);

//实例2.枚举有参
Demo7_Week Thu = Demo7_Week.Thu;
Demo7_Week Fri = Demo7_Week.Fri;
Demo7_Week Sat = Demo7_Week.Sat;
System.out.println("方式2:");
System.out.println(Thu.getWeek());

//实例3.枚举得内部匿名类
Demo7_Week1 sun = Demo7_Week1.Sun; //编译看左边,运行看右边
System.out.println("方式3:");
sun.show();
}
}

执行结果:
1
2
3
4
5
6
7
8
方式1:
com.weiyigeek.reflect.Demo7_Week@15db9742

方式2
星期四

方式3:
星期天


实例2:利用enum实现枚举类

实际案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
//Week1.java
//创建一个枚举类
package com.weiyigeek.reflect;
//枚举类本身就有私有构造得方法
public enum Week1 {
Mon,Tue,Wed("星期三");

//方式1
private Week1() {};
//方式2
private String w;
private Week1(String w) {
this.w = w;
}
public String getWeek() {
return w;
}
}


//Week2.java
package com.weiyigeek.reflect;
public enum Week2 {
MON("星期一"){
public void show() {
System.out.println("星期一");
}
},TUE("星期二"){
public void show() {
System.out.println("星期二");
}
};
private String w;
private Week2(String w) {
this.w = w;
};
private String getWeek() {
return w;
}
public abstract void show();
}


//EnumDemo.java
package com.weiyigeek.reflect;
public class EnumDemo {
public static void main(String[] args) {
//实例1.常规枚举
Week1 w = Week1.Mon;
System.out.println("方法1:");
System.out.println(w);

//实例2.有参数枚举
Week1 w1 = Week1.Wed;
System.out.println("方法2:");
System.out.println(w1.getWeek());

//实例3:抽象的枚举类
Week2 w2 = Week2.MON;
System.out.println("方法3:");
w2.show();

//实例4.与switch进行联用
System.out.println("方法4:");
Week2 w3 = Week2.MON;
switch (w3) {
case MON:
System.out.println("星期 1 ");
break;
case TUE:
System.out.println("星期 2 ");
}
}
}

运行结果:
1
2
3
4
5
6
7
8
方法1:
Mon
方法2:
星期三
方法3:
星期一
方法4:
星期 1


实例3:enum常见方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.weiyigeek.reflect;
public class EnumWeek {
public static void main(String[] args) {
//Enum 常用方法演示
Week2 mon = Week2.MON;
Week2 tue = Week2.TUE;
Week2 wed = Week2.WED;
System.out.println("枚举项都是有编号的:" + mon.ordinal() + ", " + tue.ordinal());
System.out.println("比较枚举项的序号顺序:" + mon.compareTo(tue));
System.out.println("枚举项HasCode:" + wed.hashCode());
System.out.println("枚举项名称: " + wed.name());
System.out.println("枚举项内容: " + wed.toString());

System.out.print("通过字节码码对象获取枚举项 : ");
Week2 mon1 = Week2.valueOf(Week2.class, "MON");
System.out.println(mon1);

//补充说明:Value() JDK文档中没有但是非常好用
System.out.println("Value() JDK文档中没有但是非常好用:");
Week2[] w2 = Week2.values();
for (Week2 wk : w2) {
System.out.println(wk); //这是toString的功劳
}
}
}

执行结果:
1
2
3
4
5
6
7
8
9
枚举项都是有编号的:0, 1
比较枚举项的序号顺序:-1
枚举项HasCode:366712642
枚举项名称: WED
枚举项内容: 星期三
通过字节码码对象获取枚举项 : 星期一
星期一
星期二
星期三


JDK1.7新特性

  1. 二进制字面量 (0b110) = 6
  2. 数字字面量可以出现下划线
  3. Switch 语句可以用字符串
  4. 泛型简化菱型泛型
  5. 异常多个catch合并每个异常用 或(|) 拼接
  6. try-witch-resource 语句该版本标准的异常处理代码;

实际案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.weiyigeek.reflect;
public class Demo8_JDK7Speciality {
public static void main(String[] args) {
System.out.print("二进制字面量:");
System.out.println(0b1111 + " ");
System.out.println(0b11_11);

System.out.print("数值字面量:");
System.out.println(1000_1111);
}
}
//执行结果
二进制字面量:15 15
数值字面量:10001111


JDK1.8新特性

  1. 接口中可以定义有方法体的方法,如果是非静态必须采用default修饰,如果是静态的就可不用了;
  2. 局部内部类方他所在的方法中的局部变量必须用final修饰为什么?
    • 因为当调用这个方法时候局部变量如果没有final修饰,他的生命周期和方法的生命周期是一致的,当方法弹栈这个局部变量也会消失,那么如果局部内部类对象还没马上消失想用这个局部变量就没有了。
    • 如果采用final修饰会在类加载的时候进行常量池,即使方法弹栈常量池还是存在也可以继续使用;


基础实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package com.weiyigeek.reflect;
public class Demo9_JDK8Speciality {
public static void main(String[] args) {
//JDK 8 新特性
Demo d = new Demo();
d.print();

Inter.method(); //静态方法直接调用

d.run();
}
}

interface Inter {
//非静态的一般方法必须采用Default方法进行修饰,否则报错;
public default void print() {
System.out.println("Hello World!");
}

//静态方法可以直接采用接口名称调用
public static void method() {
System.out.println("Static Method");
}
}

class Demo implements Inter {
//局部内部类,采用局部变量时在1.7以前必须添加上final修饰,但是在1.8以后就不用了;
public void run() {
int flag = 1024; //局部变量
class Inner {
public void num() {
System.out.println("flag = " + flag);
}
}
Inner i = new Inner();
i.num();
}
}

执行结果:
1
2
3
Hello World!
Static Method
flag = 1024