传智播客 2015 刘意 Java基础-视频-笔记day27(完结)(2016年5月1日12:42:20),智播客day27
传智播客 2015 刘意 Java基础-视频-笔记day27(完结)(2016年5月1日12:42:20),智播客day27
day27 1.类的加载概述和加载时机 2.类加载器的概述和分类 类加载器 负责将.class文件加载到内存中,并为之生成对应的Class对象。 虽然我们不需要关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行。 类加载器的组成 Bootstrap ClassLoader根类加载器 Extension ClassLoader扩展类加载器 SysetmClassLoader系统类加载器
通过这些描述我们就可以知道我们常用的东西的加载都是由谁来完成的。
到目前为止我们已经知道把class文件加载到内存了,那么,如果我们仅仅站在这些class文件的角度,我们如何来使用这些class文件中的内容呢?
这就是我们反射要研究的内容。
3.反射概述 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。 要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象. 所有.class文件都可以看做一个Class文件对象 反射:就是通过class文件对象,去使用该文件中的成员变量,构造方法,成员方法。 Person p = new Person(); p.使用 要想这样使用,首先你必须得到class文件对象,其实也就是得到Class类的对象。 Class类: 成员变量 Field 构造方法 Constructor 成员方法 Method ========================================================== 4.获取class文件对象的三种方式 ======================================A:三种获取Class对象的方式
1:Person p = new Person();
Class c = p.getClass();
2:Class c2 = Person.class;
任意数据类型都具备一个class静态属性,看上去要比第一种方式简单.
3:将类名作为字符串传递给Class类中的静态方法forName即可
Class c3 = Class.forName("Person");
4:第三种和前两种的区别
前两种你必须明确Person类型.
后面是你我这种类型的字符串就行.这种扩展更强.我不需要知道你的类.我只提供字符串,按照配置文件加载就可以了
==================================================================================== 获取class文件对象的方式: A:Object类的getClass()方法 B:数据类型的静态属性class C:Class类中的静态方法 public static Class forName(String className) 一般我们到底使用谁呢? A:自己玩 任选一种,第二种比较方便 B:开发 第三种 为什么呢?因为第三种是一个字符串,而不是一个具体的类名。这样我们就可以把这样的字符串配置到配置文件中。 先写Person类(类里面的东西没实际意义,只是测试用) 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 + "]"; } } ====================================================== 测试类 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); } } ======================================================== 一些注意事项 下面是获取包名的两个比较推荐的技巧 方法1 方法2 copy后 5.通过反射获取无参构造方法并使用 通过反射获取构造方法并使用。 获取公共构造方法(获取带public修饰的构造方法) // 获取字节码文件对象 Class c = Class.forName("cn.itcast_01.Person"); // 获取构造方法 public Constructor[] getConstructors():所有公共构造方法 Constructor[] cons = c.getConstructors(); for (Constructor con : cons) { System.out.println(con); } 运行如下 只获取了两个构造方法(Person类中有三个构造方法) 仔细查看Person类 ============================================================= 那么,如何获取所有的构造方法?? public Constructor[] getDeclaredConstructors():所有构造方法 // 获取字节码文件对象 Class c = Class.forName("cn.itcast_01.Person"); // 获取构造方法 // public Constructor[] getDeclaredConstructors():所有构造方法 Constructor[] cons = c.getDeclaredConstructors(); for (Constructor con : cons) { System.out.println(con); } 运行如下 获取一个构造方法 public class ReflectDemo { public static void main(String[] args) throws Exception {//异常实在是太多了,只好直接用Exception // 获取字节码文件对象 Class c = Class.forName("cn.itcast_01.Person"); // 获取单个构造方法 // public Constructor<T> getConstructor(Class<?>... parameterTypes) // 参数表示的是:你要获取的构造方法的构造参数个数及数据类型的class字节码文件对象 Constructor con = c.getConstructor();// 返回的是构造方法对象 // public T newInstance(Object... initargs) // 使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。 Object obj = con.newInstance(); System.out.println(obj); // Person p = (Person)obj; // p.show(); } } 运行如下 ===================================================== 其实上面写了那么多,就相当于下面两句话 Person p = new Person(); System.out.println(p); 那么问题来了----为什么要那么麻烦呢 原因是下图 估计是封装需要 在没有显式导入Person类的情况下悄然用了它的构造方法,而你,一无所知。 ====================================================== 再看一次无注释的代码(其实就只有几步而已)(暂时没用到泛型) 6.通过反射获取带参构造方法并使用 需求:通过反射去获取该构造方法并使用: public Person(String name, int age, String address) ============================================ 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);//注意这里不可以直接(String,int,String ) // 通过带参构造方法对象创建对象 // public T newInstance(Object... initargs) Object obj = con.newInstance("林青霞", 27, "北京"); System.out.println(obj); } } ========================================================== 7.通过反射获取私有构造方法并使用 需求:通过反射获取私有构造方法并使用 private Person(String name){} ================================================ public class ReflectDemo3 { public static void main(String[] args) throws Exception { // 获取字节码文件对象 Class c = Class.forName("cn.itcast_01.Person"); // 获取私有构造方法对象 // NoSuchMethodException:没有这个方法异常(原因是使用了getConstructor方法找不到私有的构造方法) // 原因是一开始我们使用的方法只能获取公共的,下面这种方式就可以了。 Constructor con = c.getDeclaredConstructor(String.class); // 用该私有构造方法创建对象 // IllegalAccessException:非法的访问异常。(然而即便是getDeclaredConstructor也访问不了) // 暴力访问 con.setAccessible(true);// 值为true则指示反射的对象在使用时应该取消Java语言访问检查。 Object obj = con.newInstance("风清扬"); System.out.println(obj); } } =====================================================
8.通过反射获取成员变量并使用
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); } } 运行示例 注意: addressField.set(obj, "北京"); // 给obj对象的addressField字段设置值为"北京" 即字段反过来set对象所对应它的字段值 这个与常规的set方法恰好相反,即通过对象set字段的值 ======================================================== 9.通过反射获取无参无返回值成员方法并使用
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"); // public Object invoke(Object obj,Object... args) // 返回值是Object接收,第一个参数表示对象是谁,第二参数表示调用该方法的实际参数 m1.invoke(obj); // 调用obj对象的m1方法 } }
========================================================== 下面两图显示了getMethods()与getDeclaredMethods()的区别 getMethods() getDeclaredMethods() 10.通过反射获取带参带返回值成员方法并使用 承接上一节的代码 // 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);//用Object接收 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); ========================================================= 11.通过反射运行配置文件内容 通过配置文件运行类中的方法 首先新建三个相似的类 ======================= public class Student { public void love() { System.out.println("爱生活,爱Java"); } } ======================== public class Teacher { public void love() { System.out.println("爱生活,爱青霞"); } } =========================== public class Worker { public void love() { System.out.println("爱生活,爱老婆"); } } ===================================================== 然后新建一个配置文件----class.txt ???是可变的内容,可以人为修改 ==================================================== 测试 public class Test { public static void main(String[] args) throws Exception { // 反射前的做法 // Student s = new Student(); // s.love(); // Teacher t = new Teacher(); // t.love(); // Worker w = new Worker(); // w.love(); // 反射后的做法 // 加载键值对数据 Properties prop = new Properties(); FileReader fr = new FileReader("class.txt"); prop.load(fr); fr.close(); // 获取数据 String className = prop.getProperty("className");//通过键获取值 String methodName = prop.getProperty("methodName");//同上 // 反射 Class c = Class.forName(className); Constructor con = c.getConstructor(); Object obj = con.newInstance(); // 调用方法 Method m = c.getMethod(methodName); m.invoke(obj); } } ========================================================== 下面通过修改配置文件而不动任何Test类的代码运行产生不同的效果
然后运行程序,控制台输出 爱生活,爱Java 然后,修改配置文件 然后运行程序,控制台输出 爱生活,爱青霞 再次修改配置文件 然后运行程序,控制台输出 爱生活,爱老婆 ======================================================= 还有一个注意的地方 // 加载键值对数据 Properties prop = new Properties(); FileReader fr = new FileReader("class.txt"); prop.load(fr); fr.close(); 注意load方法 12.通过反射越过泛型检查 我给你ArrayList<Integer>的一个对象,我想在这个集合中添加一个字符串数据,如何实现呢? =============================================================== public class ArrayListDemo { public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { // 创建集合对象 ArrayList<Integer> array = new ArrayList<Integer>(); // array.add("hello"); // array.add(10); Class c = array.getClass(); // 集合ArrayList的class文件对象 Method m = c.getMethod("add", Object.class); m.invoke(array, "hello"); // 调用array的add方法,传入的值是hello m.invoke(array, "world"); m.invoke(array, "java"); System.out.println(array); } } =================================================== 下面是一些说明 然后利用xJad反编译ArrayListDemo 可以通过查源码发现ArrayList默认接收Object类型的参数 13.通过反射写一个通用的设置某个对象的某个属性为指定的值 先建一个Tool类 public class Tool { public void setProperty(Object obj, String propertyName, Object value) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { // 根据对象获取字节码文件对象 Class c = obj.getClass(); // 获取该对象的propertyName成员变量 Field field = c.getDeclaredField(propertyName); // 取消访问检查 field.setAccessible(true); // 给对象的成员变量赋值为指定的值 field.set(obj, value); } } ============================================== 上面的Tool类非常强大,通过改变三个参数的值可以给任意一个类中的任意一个成员变量进行赋值,即使这个成员变量是私有的 下面是测试类 public class ToolDemo { public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { Person p = new Person(); Tool t = new Tool(); t.setProperty(p, "name", "林青霞"); t.setProperty(p, "age", 27); System.out.println(p); System.out.println("-----------"); Dog d = new Dog(); t.setProperty(d, "sex", ‘男‘); t.setProperty(d, "price", 12.34f); System.out.println(d); } } class Dog { char sex; float price; @Override public String toString() { return sex + "---" + price; } } class Person { private String name; public int age; @Override public String toString() { return name + "---" + age; } } =============================================================== 14.动态代理的概述和实现(这块信息量有点大,所以略写了。。) 动态代理引入----为什么需要动态代理 如下图所示,其实动态代理帮一个已经不能改动的程序添加一些附加功能,相当于中介 例如下图的权限校验和日志记录本来夹在添加功能之中,通过动态代理把附加的功能--权限校验和日志记录与核心功能--添加功能分开 动态代理 下图中cglib是框架才学的内容
Proxy类中创建动态代理对象的方法的三个参数;
ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的invoke 方法来进行调用。
InvocationHandler接口中invoke方法的三个参数:
proxy:代表动态代理对象
method:代表正在执行的方法(在本例中指的是add,delete,update,find方法)
args:代表调用目标方法时传入的实参
Proxy.newProxyInstance
创建的代理对象是在jvm运行时动态生成的一个对象,它并不是我们的InvocationHandler类型,
也不是我们定义的那组接口的类型,而是在运行是动态生成的一个对象,并且命名方式都是这样的形式,
以$开头,proxy为中,最后一个数字表示对象的标号。
System.out.println(u.getClass().getName());
================================================================ 下面是完整代码 UserDao接口 /* * 用户操作接口 */ public interface UserDao { public abstract void add(); public abstract void delete(); public abstract void update(); public abstract void find(); } ============================= UserDaoImpl 实现类 public class UserDaoImpl implements UserDao { @Override public void add() { System.out.println("添加功能"); } @Override public void delete() { System.out.println("删除功能"); } @Override public void update() { System.out.println("修改功能"); } @Override public void find() { System.out.println("查找功能"); } } ==================================== StudentDao 接口 public interface StudentDao { public abstract void login(); public abstract void regist(); } =================================== 实现类StudentDaoImpl public class StudentDaoImpl implements StudentDao { @Override public void login() { System.out.println("登录功能"); } @Override public void regist() { System.out.println("注册功能"); } } ================================== 注意上面的两个实现类并没有加入权限校验还有日志记录 MyInvocationHandler 类 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("权限校验"); Object result = method.invoke(target, args); System.out.println("日志记录"); return result; // 返回的是代理对象 } } ============================================= 测试类 public class Test { public static void main(String[] args) { UserDao ud = new UserDaoImpl(); ud.add(); ud.delete(); ud.update(); ud.find(); System.out.println("-----------"); // 我们要创建一个动态代理对象 // Proxy类中有一个方法可以创建动态代理对象 // public static Object newProxyInstance(ClassLoader loader,Class<?>[] // interfaces,InvocationHandler h) // 我准备对ud对象做一个代理对象 MyInvocationHandler handler = new MyInvocationHandler(ud);//接收参数ud,代表ud需要代理 UserDao proxy = (UserDao) Proxy.newProxyInstance(ud.getClass() .getClassLoader(), ud.getClass().getInterfaces(), handler);//不强转的话是Object类型 //其实重点关注第三个参数handler就行了,前两个参数格式都是固定的,目前没必要纠结 proxy.add(); proxy.delete(); proxy.update(); proxy.find(); System.out.println("-----------"); StudentDao sd = new StudentDaoImpl(); MyInvocationHandler handler2 = new MyInvocationHandler(sd); StudentDao proxy2 = (StudentDao) Proxy.newProxyInstance(sd.getClass() .getClassLoader(), sd.getClass().getInterfaces(), handler2); proxy2.login(); proxy2.regist(); } } 运行示例 =================================================== 下面是一些解析 对于关键的MyInvocationHandler类的解读 关键 15.模版设计模式概述和使用 GetTime类(抽象类) public abstract class GetTime { // 需求:请给我计算出一段代码的运行时间 public long getTime() { long start = System.currentTimeMillis(); // 再给我测试一个代码:集合操作的,多线程操作,常用API操作的等等... code();//抽象了多种可能变化的需要测试运行时间的代码 long end = System.currentTimeMillis(); return end - start; } public abstract void code(); } ==================================================== 接着创建一个继承上面抽象类的具体类 比如,要测试for循环运行时间 public class ForDemo extends GetTime { @Override public void code() { for (int x = 0; x < 100000; x++) { System.out.println(x); } } } ==================================================== 比如说,我还想测试IO流的运行时间 就再创建一个类 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(); } } } ========================================================== 最后,测试类 public class GetTimeDemo { public static void main(String[] args) { GetTime gt = new ForDemo();//多态 System.out.println(gt.getTime() + "毫秒"); gt = new IODemo();//多态 System.out.println(gt.getTime() + "毫秒"); } } ==================================================== 运行(部分)示例
一个解析
16.装饰模式概述和使用
图解装饰者模式
下面是代码(写的时候多参照上面的图解,提供了思路) 首先Phone接口 public interface Phone { public abstract void call(); } ============================== 实现这个接口(具体手机) public class IPhone implements Phone { @Override public void call() { System.out.println("手机可以打电话了"); } } ================================ 然后,一个抽象装饰类,实现Phone接口 public abstract class PhoneDecorate implements Phone { private Phone p; public PhoneDecorate(Phone p) { this.p = p; } @Override public void call() { this.p.call();//特别注意一下这里 } } =========================================== 然后,具体的装饰类 public class RingPhoneDecorate extends PhoneDecorate { public RingPhoneDecorate(Phone p) { super(p); } @Override public void call() { System.out.println("手机可以听彩铃"); super.call();//听彩铃在前,顺序不要乱了 } } ============================================= 另一个具体的装饰类 public class MusicPhoneDecorate extends PhoneDecorate { public MusicPhoneDecorate(Phone p) { super(p); } @Override public void call() { super.call(); System.out.println("手机可以听音乐");//接电话后听音乐,顺序不要反了 } } ================================================ 最后 测试类 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流中用得很多 // 想想我们在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);//相对简单一点的组合 ================================================== 最后是两个解析 17.JDK5新特性回顾 18.自己实现枚举类(由简单到复杂) 枚举概述 是指将变量的值一一列出来,变量的值只限于列举出来的值的范围内。举例:一周只有7天,一年只有12个月等。 回想单例设计模式:单例类是一个类只有一个实例 那么多例类就是一个类有多个实例,但不是无限个数的实例,而是有限个数的实例。这才能是枚举类。 ========================================================================== 简单版 public class Direction { // 创建几个实例 public static final Direction FRONT = new Direction(); public static final Direction BEHIND = new Direction(); public static final Direction LEFT = new Direction(); public static final Direction RIGHT = new Direction(); // 构造私有,别人就不能无限的创建了 private Direction() { } } ================================================== 稍微复杂 public class Direction2 { // 创建几个实例 public static final Direction2 FRONT = new Direction2("前"); public static final Direction2 BEHIND = new Direction2("后"); public static final Direction2 LEFT = new Direction2("左"); public static final Direction2 RIGHT = new Direction2("右"); // 构造私有,别人就不能无限的创建了 // private Direction2() { // } // 加入成员变量,并去掉无参构造 private String name; private Direction2(String name) { this.name = name; } public String getName() { return name; } } ==================================================== 最复杂(在这个抽象类内部,FRONT ,BEHIND ,LEFT ,RIGHT 是Direction3 抽象类的具体实现子类,并实现(重写)了show方法)(也就是利用匿名内部类在一个抽象类里含有实现了这个抽象类的子类) public abstract class Direction3 { // 创建几个实例 public static final Direction3 FRONT = new Direction3("前") { @Override public void show() { System.out.println("前"); } };//利用匿名内部类使得一个抽象类内含有实现该类的子类 public static final Direction3 BEHIND = new Direction3("后") { @Override public void show() { System.out.println("后"); } }; public static final Direction3 LEFT = new Direction3("左") { @Override public void show() { System.out.println("左"); } }; public static final Direction3 RIGHT = new Direction3("右") { @Override public void show() { System.out.println("右"); } }; // 加入成员变量,并去掉无参构造 private String name; private Direction3(String name) { this.name = name; } public String getName() { return name; } // 加入抽象方法 public abstract void show();//由于含有了抽象方法,所以这个类只能改为抽象类 } ========================================== 对以上三个类的测试类 public class DirectionDemo { public static void main(String[] args) { Direction d = Direction.FRONT; System.out.println(d); System.out.println("------------------------------------"); Direction2 d2 = Direction2.FRONT; System.out.println(d2); System.out.println(d2.getName()); d2 = Direction2.RIGHT; System.out.println(d2); System.out.println(d2.getName()); System.out.println("------------------------------------"); Direction3 d3 = Direction3.FRONT; System.out.println(d3); System.out.println(d3.getName()); d3.show(); d3 = Direction3.LEFT; System.out.println(d3); System.out.println(d3.getName()); d3.show(); } } ======================================= 19.通过enum实现枚举类 最常见格式 /* * 通过JDK5提供的枚举来做枚举类 */ public enum Direction { FRONT, BEHIND, LEFT, RIGHT;//逗号隔开 } 反编译一下
下面从难到易列三个版本 新建一个枚举类 提示:枚举类已经自动重写toString方法(打印的不再是地址,而是FRONT,或者BEHIND,或者LEFT,或者RIGHT) 简单版 /* * 通过JDK5提供的枚举来做枚举类 */ public enum Direction { FRONT, BEHIND, LEFT, RIGHT;//其实这几个相当于无参构造方法 } =========================== 稍微复杂版 public enum Direction2 { FRONT("前"), BEHIND("后"), LEFT("左"), RIGHT("右");//其实这几个相当于带参构造方法 private String name; private Direction2(String name) { //这里不能为public this.name = name; } public String getName() { return name; } } =================================== 复杂版本 public enum Direction3 { FRONT("前") { @Override public void show() { System.out.println("前"); } }, BEHIND("后") { @Override public void show() { System.out.println("后"); } }, LEFT("左") { @Override public void show() { System.out.println("左"); } }, RIGHT("右") { @Override public void show() { System.out.println("右"); } }; private String name; private Direction3(String name) {//这里不能为public this.name = name; } public String getName() { return name; } public abstract void show(); } ============================================== 一个解析 20.枚举的注意事项 枚举在switch语句中的使用 例如在测试类当中 Direction3 dd = Direction3.FRONT;//此时dd的值是FRONT dd = Direction3.LEFT; switch (dd) { case FRONT: System.out.println("你选择了前"); break; case BEHIND: System.out.println("你选择了后"); break; case LEFT: System.out.println("你选择了左"); break; case RIGHT: System.out.println("你选择了右"); break; =============================================== 21.枚举类的常见方式 public class EnumMethodDemo { public static void main(String[] args) { // int compareTo(E o) Direction2 d21 = Direction2.FRONT; Direction2 d22 = Direction2.BEHIND; Direction2 d23 = Direction2.LEFT; Direction2 d24 = Direction2.RIGHT; System.out.println(d21.compareTo(d21)); System.out.println(d21.compareTo(d24)); System.out.println(d24.compareTo(d21)); System.out.println("---------------"); // String name() System.out.println(d21.name()); System.out.println(d22.name()); System.out.println(d23.name()); System.out.println(d24.name()); System.out.println("--------------"); // int ordinal() System.out.println(d21.ordinal()); System.out.println(d22.ordinal()); System.out.println(d23.ordinal()); System.out.println(d24.ordinal()); System.out.println("--------------"); // String toString() System.out.println(d21.toString()); System.out.println(d22.toString()); System.out.println(d23.toString()); System.out.println(d24.toString()); System.out.println("--------------"); // <T> T valueOf(Class<T> type,String name) Direction2 d = Enum.valueOf(Direction2.class, "FRONT"); System.out.println(d.getName()); System.out.println("----------------"); // values() // 此方法虽然在JDK文档中查找不到,但每个枚举类都具有该方法,它遍历枚举类的所有枚举值非常方便 Direction2[] dirs = Direction2.values(); for (Direction2 d2 : dirs) { System.out.println(d2); System.out.println(d2.getName()); } } } ==================================================== 运行示例
单独说明---compareTo运行 很明显,是按照先后顺序做减法来compare的,排名靠后的减去(compareTo)排名靠前的为正数 反之负数,相同则为0 22.JDK7的六个新特性回顾和讲解 测试 // 二进制字面量 int x = 0b100101; System.out.println(x); // 数字字面量可以出现下划线 int y = 1_1123_1000; // 不能出现在进制标识和数值之间 int z = 0x111_222; // 不能出现在数值开头和结尾 int a = 0x11_22; // 不能出现在小数点旁边 double d = 12.3_4;//这个是允许的 // switch 语句可以用字符串?自己回顾 // 泛型简化 ArrayList<String> array = new ArrayList<>(); =========================================== 下面是一个要注意的JDK1.7新特性 可利用反编译工具查看源码 在普通的try catch语句中,在try后面添加一个小括号,里面放上需要实现自动关闭和代码,然后就可以了 // 异常的多个catch合并 private static void method() { // try-with-resources 语句 // try(必须是java.lang.AutoCloseable的子类对象){…} try { //这个是普通的try catch语句 FileReader fr = new FileReader("a.txt"); FileWriter fw = new FileWriter("b.txt"); int ch = 0; while ((ch = fr.read()) != -1) { fw.write(ch); } fw.close(); fr.close(); } catch (IOException e) { e.printStackTrace(); } // 改进版的代码,利用新特性 try (FileReader fr = new FileReader("a.txt"); FileWriter fw = new FileWriter("b.txt");) {//红字为新特性添加的内容,然后流就不用手动写代码关闭了 int ch = 0; while ((ch = fr.read()) != -1) { fw.write(ch); } } catch (IOException e) { e.printStackTrace(); } } 也就是 ====================================================== 注意一下这个新特性的使用条件,范围(必须是实现AutoCloseable接口的对象) 23.JDK1.8-----接口中也可以有方法了 资料---------[Java] Java 8的新特性,让我欢喜让我忧。 Java 8的新特性,让我欢喜让我忧。 http://bbs.itcast.cn/thread-24398-1-1.html (出处: 传智播客论坛_传智播客旗下社区) ================================================ Java 8或许是迄今为止最令人期待的Java版本,最初定于2013年的9月份发布, 但由于一系列的安全漏洞问题,最终在2014年的3月18日才发布了正式版。 作为同门兄弟,NetBeans 8.0跟JDK 8同时发布,在版本号上也与JDK保持一致。 看来,关键时刻还得靠兄弟啊。呵呵。 下载链接: http://www.oracle.com/technetwork/java/javase/downloads/index.html 那么,在经过这么长的时间闭关修炼后,Java 8到底为我们带来了什么呢? 从官方的一些介绍中,大致的有如下的新特性: 1:允许在接口中有默认方法实现 2:Lambda表达式 3:函数式接口 4:内置函数式接口 5:Streams和 6:Map 7:时间日期API 8:Annotations 如果有同学或者朋友在自己的新项目中使用到Java 8,如果有新的发现,或者问题, 可以在我们论坛提出问题交流。共同推进Java语言的发展和进步。 下面我仅仅就其中几个比较有意思的写个代码简单测试一下。 1:允许在接口中有默认方法实现 可以使用default关键字和static关键字修饰方法并给出方法体。 代码如下: /* Java 8 允许我们使用default关键字,为接口声明添加非抽象的方法实现。 这个特性又被称为扩展方法。除此以外,Java 8还允许为接口声明添加静态的方法实现, 但是,在使用的时候只能通过接口名称去限定使用。 */ //接口 interface Inter { //抽象方法 public abstract void show(); //default方法 public default void defaultPrint() { System.out.println("defaultPrint 我爱林青霞"); } //static方法 public static void staticPrint() { System.out.println("staticPrint 我爱林青霞"); } } //实现类 class InterImpl implements Inter { public void show() { System.out.println("重写接口中的方法"); } } //测试类 public class Demo01 { public static void main(String[] args) { Inter i = new InterImpl(); i.show(); i.defaultPrint(); //如下使用是错误的 //i.staticPrint(); //错误: 静态接口方法调用非法,应将接收方表达式替换为类型限定符 ‘Inter‘ //正确用法 Inter.staticPrint(); } } 2:Lambda表达式 "Lambda 表达式"(lambda expression)是一个匿名函数,即没有函数名的函数。 Lambda表达式可以表示闭包(注意和数学传统意义上的不同)。 代码如下: import java.util.List; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; /* 需求:对多个字符串进行排序。 我将分别采用Java 8之前的解决方案和Java 8之后的解决方案,大家自己看那个好。 */ public class Demo02 { public static void main(String[] args) { String[] strArray = {"hello","world","java"}; //调用Java 8之前的解决方案 stringSort(strArray); //遍历数组 for(String str : strArray) { System.out.println(str); } System.out.println("-----------------------"); //调用Java 8之后的解决方案 stringSort2(strArray); //遍历数组 for(String str : strArray) { System.out.println(str); } } //Java 8之前的解决方案。 public static void stringSort(String[] strArray) { //把字符串数组转成集合 List<String> list = Arrays.asList(strArray); //使用集合工具类的排序功能 Collections.sort(list, new Comparator<String>() { @Override public int compare(String s1, String s2) { return s1.compareTo(s2); } }); } //Java 8之后的解决方案,Lambda表达式(如果不知道是什么,自己先google下) public static void stringSort2(String[] strArray) { //把字符串数组转成集合 List<String> list = Arrays.asList(strArray); //使用集合工具类的排序功能,采用Lambda表达式实现 //Collections.sort(list, (String s1, String s2) -> { //return s1.compareTo(s2); //}); //通过上面的代码,你可以发现它比我们以前的方式要简洁。 //但是,它还可以更简洁 //只要一行代码,包含了方法体。你甚至可以连大括号对{}和return关键字都省略不要。 //Collections.sort(list, (String s1, String s2) -> s1.compareTo(s2)); //但是这还不是最简洁的,最终版代码如下: Collections.sort(list, (s1, s2) -> s1.compareTo(s2)); //Java编译器能够自动识别参数的类型,所以你就可以省略掉类型不写。 } } 3:函数式接口和内置函数式接口 代码如下: /* 函数式接口: Lambda表达式是如何匹配Java中的类型的呢? 每一个lambda都能够通过一个特定的接口,与一个给定的类型进行匹配。 一个所谓的函数式接口必须要有且仅有一个抽象方法声明。 每个与之对应的lambda表达式必须要与抽象方法的声明相匹配。 由于默认方法不是抽象的,因此你可以在你的函数式接口里任意添加默认方法。 内置函数式接口: JDK 1.8 API中包含了很多内置的函数式接口。有些是在以前版本的Java中大家耳熟能详的, 例如Comparator接口,或者Runnable接口。对这些现成的接口进行实现, 可以通过@FunctionalInterface 标注来启用Lambda功能支持。 Comparator接口在Java 7时候的源码 : package java.util; public interface Comparator<T> { int compare(T o1, T o2); boolean equals(Object obj); } Comparator接口在Java 8时候的源码: package java.util; import java.io.Serializable; import java.util.function.Function; import java.util.function.ToIntFunction; import java.util.function.ToLongFunction; import java.util.function.ToDoubleFunction; import java.util.Comparators; @FunctionalInterface public interface Comparator<T> { int compare(T o1, T o2); boolean equals(Object obj); default Comparator<T> reversed() { return Collections.reverseOrder(this); } default Comparator<T> thenComparing(Comparator<? super T> other) { Objects.requireNonNull(other); return (Comparator<T> & Serializable) (c1, c2) -> { int res = compare(c1, c2); return (res != 0) ? res : other.compare(c1, c2); }; } default <U> Comparator<T> thenComparing( Function<? super T, ? extends U> keyExtractor, Comparator<? super U> keyComparator) { return thenComparing(comparing(keyExtractor, keyComparator)); } default <U extends Comparable<? super U>> Comparator<T> thenComparing( Function<? super T, ? extends U> keyExtractor) { return thenComparing(comparing(keyExtractor)); } default Comparator<T> thenComparingInt(ToIntFunction<? super T> keyExtractor) { return thenComparing(comparingInt(keyExtractor)); } default Comparator<T> thenComparingLong(ToLongFunction<? super T> keyExtractor) { return thenComparing(comparingLong(keyExtractor)); } default Comparator<T> thenComparingDouble(ToDoubleFunction<? super T> keyExtractor) { return thenComparing(comparingDouble(keyExtractor)); } public static <T extends Comparable<? super T>> Comparator<T> reverseOrder() { return Collections.reverseOrder(); } @SuppressWarnings("unchecked") public static <T extends Comparable<? super T>> Comparator<T> naturalOrder() { return (Comparator<T>) Comparators.NaturalOrderComparator.INSTANCE; } public static <T> Comparator<T> nullsFirst(Comparator<? super T> comparator) { return new Comparators.NullComparator<>(true, comparator); } public static <T> Comparator<T> nullsLast(Comparator<? super T> comparator) { return new Comparators.NullComparator<>(false, comparator); } public static <T, U> Comparator<T> comparing( Function<? super T, ? extends U> keyExtractor, Comparator<? super U> keyComparator) { Objects.requireNonNull(keyExtractor); Objects.requireNonNull(keyComparator); return (Comparator<T> & Serializable) (c1, c2) -> keyComparator.compare(keyExtractor.apply(c1), keyExtractor.apply(c2)); } public static <T, U extends Comparable<? super U>> Comparator<T> comparing( Function<? super T, ? extends U> keyExtractor) { Objects.requireNonNull(keyExtractor); return (Comparator<T> & Serializable) (c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2)); } public static <T> Comparator<T> comparingInt(ToIntFunction<? super T> keyExtractor) { Objects.requireNonNull(keyExtractor); return (Comparator<T> & Serializable) (c1, c2) -> Integer.compare(keyExtractor.applyAsInt(c1), keyExtractor.applyAsInt(c2)); } public static <T> Comparator<T> comparingLong(ToLongFunction<? super T> keyExtractor) { Objects.requireNonNull(keyExtractor); return (Comparator<T> & Serializable) (c1, c2) -> Long.compare(keyExtractor.applyAsLong(c1), keyExtractor.applyAsLong(c2)); } public static<T> Comparator<T> comparingDouble(ToDoubleFunction<? super T> keyExtractor) { Objects.requireNonNull(keyExtractor); return (Comparator<T> & Serializable) (c1, c2) -> Double.compare(keyExtractor.applyAsDouble(c1), keyExtractor.applyAsDouble(c2)); } } 此外,Java 8 API 还提供了很多新的函数式接口,来降低程序员的工作负担。 下面我们通过一个案例来学习Java 8内置的函数式接口的使用。 */ import java.util.ArrayList; import java.util.List; public class Demo03 { public static void main(String[] args) { //创建集合对象 List<String> list = new ArrayList<String>(); //添加元素 list.add("hello"); list.add("world"); list.add("java"); list.add("itcast"); list.add("javase"); list.add("javaee"); list.add("android"); //filter接受一个predicate接口类型的变量,并将所有流对象中的元素进行过滤。 //list.stream().filter((s) -> s.startsWith("j")).forEach(System.out::println); //sorted是一个中间操作,能够返回一个排过序的流对象的视图。 //流对象中的元素会默认按照自然顺序进行排序, //除非你自己指定一个Comparator接口来改变排序规则。 //list.stream().sorted().filter((s) -> s.startsWith("j")).forEach(System.out::println); //map是一个对于流对象的中间操作,通过给定的方法, //它能够把流对象中的每一个元素对应到另外一个对象上。 list.stream().map(String::toUpperCase).sorted((a, b) -> b.compareTo(a)).forEach(System.out::println); //基本思路就是:把list中的元素转成大写,倒序,并输出。 //看看这种做法,和以前要实现这种做法的区别。现在这种做法是不是太好了。 } } 欢迎大家多讨论,交流。祝同学们学业有成。 ============================================================ 以上就是资料 下面是测试代码 以下代码全部在一个叫做Demo01.java文件中 =========================================================== interface Inter { //抽象方法 public abstract void show(); //default方法 public default void defaultPrint() { System.out.println("defaultPrint 我爱林青霞"); } //static方法 public static void staticPrint() { System.out.println("staticPrint 我爱林青霞"); } } //实现类 class InterImpl implements Inter { public void show() { System.out.println("重写接口中的方法"); } } //测试类 public class Demo01 { public static void main(String[] args) { //Inter.defaultPrint(); //非静态方法不能直接使用 Inter.staticPrint(); Inter i = new InterImpl(); i.defaultPrint(); i.show(); } } 注意 =========================================================== day27笔记补充 装饰设计模式 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); Scanner sc = new Scanner(System.in); JDK新特性 (1)JDK5(掌握) 装箱和拆箱 泛型 增强for 静态导入 可变参数 枚举 (2)JDK6(了解) (3)JDK7(理解) 二进制的表现形式 用_分隔数据 switch语句可是用字符串 泛型推断(菱形泛型) 多catch的使用 自动释放资源的用法 (4)JDK8(了解) 可以去网上了解资料
传智播客 2015 刘意 Java基础-视频-笔记day27(完结)(2016年5月1日12:42:20)
相关文章
- 暂无相关文章
用户点评