精华内容
下载资源
问答
  • java 高级特性

    2008-09-26 11:25:00
    1,关于jdk内存泄漏的问题http://blogs.sun.com/fkieviet/entry/classloader_leaks_the_dreaded_java2.关于classloader的加载http://www.theserverside.com/tt/articles/article.tss?l=ClassLoading
    1,关于jdk内存泄漏的问题
    http://blogs.sun.com/fkieviet/entry/classloader_leaks_the_dreaded_java
    2.关于classloader的加载
    http://www.theserverside.com/tt/articles/article.tss?l=ClassLoading
    展开全文
  • Java高级特性—反射

    千次阅读 2020-01-23 17:22:11
    Java高级特性——反射 原文链接 定义 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的...

    推荐:Java设计模式汇总

    Java高级特性—反射

    原文链接

    定义
    JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。

    用途
    在日常的第三方应用开发过程中,经常会遇到某个类的某个成员变量、方法或是属性是私有的或是只对系统应用开放,这时候就可以利用Java反射机制通过反射来获取所需的私有成员或是方法。当然,也不是所有的都适合反射,之前就遇到一个案例,通过反射得到的结果与预期不符。阅读源码发现,经过层层调用后在最终返回结果的地方对应用的权限进行了校验,对于没有权限的应用返回值是没有意义的缺省值,否则返回实际值,起到了保护用户的隐私目的。

    反射机制的相关类
    与Java反射相关的类如下:

    类名 用途
    Class类 代表类的实体,在运行的Java应用程序中表示类和接口
    Field类 代表类的成员变量(成员变量也称为类的属性)
    Method类 代表类的方法
    Constructor类 代表类的构造方法

    Class类
    Class代表类的实体,在运行的Java应用程序中表示类和接口。在这个类中提供了很多有用的方法,这里对他们简单的分类介绍。
    获得类相关的方法

    方法 用途
    asSubclass(Class clazz) 把传递的类的对象转换成代表其子类的对象
    Cast() 把对象转换成代表类或是接口的对象
    getClassLoader() 获得类的加载器
    getClasses() 返回一个数组,数组中包含该类中所有公共类和接口类的对象
    getDeclaredClasses() 返回一个数组,数组中包含该类中所有类和接口类的对象
    forName(String className) 根据类名返回类的对象
    getName() 获得类的完整路径名字
    newInstance() 创建类的实例
    getPackage() 获得类的包
    getSimpleName() 获得类的名字
    getSuperclass() 获得当前类继承的父类的名字
    getInterfaces() 获得当前类实现的类或是接口

    获得类中属性相关的方法

    方法 用途
    getField(String name) 获得某个公有的属性对象
    getFields() 获得所有公有的属性对象
    getDeclaredField(String name) 获得某个属性对象
    getDeclaredFields() 获得所有属性对象

    获得类中注解相关的方法

    方法 用途
    getAnnotation(Class< A > annotationClass) 返回该类中与参数类型匹配的公有注解对象
    getAnnotations() 返回该类所有的公有注解对象
    getDeclaredAnnotation(Class< A > annotationClass) 返回该类中与参数类型匹配的所有注解对象
    getDeclaredAnnotations() 返回该类所有的注解对象

    获得类中构造器相关的方法

    方法 用途
    getConstructor(Class…<?> parameterTypes) 获得该类中与参数类型匹配的公有构造方法
    getConstructors() 获得该类的所有公有构造方法
    getDeclaredConstructor(Class…<?> parameterTypes) 获得该类中与参数类型匹配的构造方法
    getDeclaredConstructors() 获得该类所有构造方法

    获得类中方法相关的方法

    方法 用途
    getMethod(String name, Class…<?> parameterTypes) 获得该类某个公有的方法
    getMethods() 获得该类所有公有的方法
    getDeclaredMethod(String name, Class…<?> parameterTypes) 获得该类某个方法
    getDeclaredMethods() 获得该类所有方法

    类中其他重要的方法

    方法 用途
    isAnnotation() 如果是注解类型则返回true
    isAnnotationPresent(Class<? extends Annotation> annotationClass) 如果是指定类型注解类型则返回true
    isAnonymousClass() 如果是匿名类则返回true
    isArray() 如果是一个数组类则返回true
    isEnum() 如果是枚举类则返回true
    isInstance(Object obj) 如果obj是该类的实例则返回true
    isInterface() 如果是接口类则返回true
    isLocalClass() 如果是局部类则返回true
    isMemberClass() 如果是内部类则返回true

    Field类
    Field代表类的成员变量(成员变量也称为类的属性)。

    方法 用途
    equals(Object obj) 属性与obj相等则返回true
    get(Object obj) 获得obj中对应的属性值
    set(Object obj, Object value) 设置obj中对应属性值

    Method类
    Method代表类的方法。

    方法 用途
    invoke(Object obj, Object… args) 传递object对象及参数调用该对象对应的方法

    Constructor类
    Constructor代表类的构造方法。

    方法 用途
    newInstance(Object… initargs) 根据传递的参数创建类的对象

    示例
    为了演示反射的使用,首先构造一个与书籍相关的model——Book.java,然后通过反射方法示例创建对象、反射私有构造方法、反射私有属性、反射私有方法,最后给出两个比较复杂的反射示例——获得当前ZenMode和关机Shutdown。

    被反射类Book.java:

    public class Book{
        private final static String TAG = "BookTag";
    
        private String name;
        private String author;
    
        @Override
        public String toString() {
            return "Book{" +
                    "name='" + name + '\'' +
                    ", author='" + author + '\'' +
                    '}';
        }
    
        public Book() {
        }
    
        private Book(String name, String author) {
            this.name = name;
            this.author = author;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getAuthor() {
            return author;
        }
    
        public void setAuthor(String author) {
            this.author = author;
        }
    
        private String declaredMethod(int index) {
            String string = null;
            switch (index) {
                case 0:
                    string = "I am declaredMethod 1 !";
                    break;
                case 1:
                    string = "I am declaredMethod 2 !";
                    break;
                default:
                    string = "I am declaredMethod 1 !";
            }
    
            return string;
        }
    }
    

    反射逻辑封装在ReflectClass.java:

    public class ReflectClass {
        private final static String TAG = "peter.log.ReflectClass";
    
        // 创建对象
        public static void reflectNewInstance() {
            try {
                Class<?> classBook = Class.forName("com.android.peter.reflectdemo.Book");
                Object objectBook = classBook.newInstance();
                Book book = (Book) objectBook;
                book.setName("Android进阶之光");
                book.setAuthor("刘望舒");
                Log.d(TAG,"reflectNewInstance book = " + book.toString());
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    
        // 反射私有的构造方法
        public static void reflectPrivateConstructor() {
            try {
                Class<?> classBook = Class.forName("com.android.peter.reflectdemo.Book");
                Constructor<?> declaredConstructorBook = classBook.getDeclaredConstructor(String.class,String.class);
                declaredConstructorBook.setAccessible(true);
                Object objectBook = declaredConstructorBook.newInstance("Android开发艺术探索","任玉刚");
                Book book = (Book) objectBook;
                Log.d(TAG,"reflectPrivateConstructor book = " + book.toString());
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    
        // 反射私有属性
        public static void reflectPrivateField() {
            try {
                Class<?> classBook = Class.forName("com.android.peter.reflectdemo.Book");
                Object objectBook = classBook.newInstance();
                Field fieldTag = classBook.getDeclaredField("TAG");
                fieldTag.setAccessible(true);
                String tag = (String) fieldTag.get(objectBook);
                Log.d(TAG,"reflectPrivateField tag = " + tag);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    
        // 反射私有方法
        public static void reflectPrivateMethod() {
            try {
                Class<?> classBook = Class.forName("com.android.peter.reflectdemo.Book");
                Method methodBook = classBook.getDeclaredMethod("declaredMethod",int.class);
                methodBook.setAccessible(true);
                Object objectBook = classBook.newInstance();
                String string = (String) methodBook.invoke(objectBook,0);
    
                Log.d(TAG,"reflectPrivateMethod string = " + string);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    
        // 获得系统Zenmode值
        public static int getZenMode() {
            int zenMode = -1;
            try {
                Class<?> cServiceManager = Class.forName("android.os.ServiceManager");
                Method mGetService = cServiceManager.getMethod("getService", String.class);
                Object oNotificationManagerService = mGetService.invoke(null, Context.NOTIFICATION_SERVICE);
                Class<?> cINotificationManagerStub = Class.forName("android.app.INotificationManager$Stub");
                Method mAsInterface = cINotificationManagerStub.getMethod("asInterface",IBinder.class);
                Object oINotificationManager = mAsInterface.invoke(null,oNotificationManagerService);
                Method mGetZenMode = cINotificationManagerStub.getMethod("getZenMode");
                zenMode = (int) mGetZenMode.invoke(oINotificationManager);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
    
            return zenMode;
        }
    
        // 关闭手机
        public static void shutDown() {
            try {
                Class<?> cServiceManager = Class.forName("android.os.ServiceManager");
                Method mGetService = cServiceManager.getMethod("getService",String.class);
                Object oPowerManagerService = mGetService.invoke(null,Context.POWER_SERVICE);
                Class<?> cIPowerManagerStub = Class.forName("android.os.IPowerManager$Stub");
                Method mShutdown = cIPowerManagerStub.getMethod("shutdown",boolean.class,String.class,boolean.class);
                Method mAsInterface = cIPowerManagerStub.getMethod("asInterface",IBinder.class);
                Object oIPowerManager = mAsInterface.invoke(null,oPowerManagerService);
                mShutdown.invoke(oIPowerManager,true,null,true);
    
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    
        public static void shutdownOrReboot(final boolean shutdown, final boolean confirm) {
            try {
                Class<?> ServiceManager = Class.forName("android.os.ServiceManager");
                // 获得ServiceManager的getService方法
                Method getService = ServiceManager.getMethod("getService", java.lang.String.class);
                // 调用getService获取RemoteService
                Object oRemoteService = getService.invoke(null, Context.POWER_SERVICE);
                // 获得IPowerManager.Stub类
                Class<?> cStub = Class.forName("android.os.IPowerManager$Stub");
                // 获得asInterface方法
                Method asInterface = cStub.getMethod("asInterface", android.os.IBinder.class);
                // 调用asInterface方法获取IPowerManager对象
                Object oIPowerManager = asInterface.invoke(null, oRemoteService);
                if (shutdown) {
                    // 获得shutdown()方法
                    Method shutdownMethod = oIPowerManager.getClass().getMethod(
                            "shutdown", boolean.class, String.class, boolean.class);
                    // 调用shutdown()方法
                    shutdownMethod.invoke(oIPowerManager, confirm, null, false);
                } else {
                    // 获得reboot()方法
                    Method rebootMethod = oIPowerManager.getClass().getMethod("reboot",
                            boolean.class, String.class, boolean.class);
                    // 调用reboot()方法
                    rebootMethod.invoke(oIPowerManager, confirm, null, false);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    

    调用相应反射逻辑方法:

            try {
                // 创建对象
                ReflectClass.reflectNewInstance();
    
                // 反射私有的构造方法
                ReflectClass.reflectPrivateConstructor();
    
                // 反射私有属性
                ReflectClass.reflectPrivateField();
    
                // 反射私有方法
                ReflectClass.reflectPrivateMethod();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
    
            Log.d(TAG," zenmode = " + ReflectClass.getZenMode());
    

    Log输出结果如下:

    08-27 15:11:37.999 11987-11987/com.android.peter.reflectdemo D/peter.log.ReflectClass: reflectNewInstance book = Book{name='Android进阶之光', author='刘望舒'}
    08-27 15:11:38.000 11987-11987/com.android.peter.reflectdemo D/peter.log.ReflectClass: reflectPrivateConstructor book = Book{name='Android开发艺术探索', author='任玉刚'}
    08-27 15:11:38.000 11987-11987/com.android.peter.reflectdemo D/peter.log.ReflectClass: reflectPrivateField tag = BookTag
    08-27 15:11:38.000 11987-11987/com.android.peter.reflectdemo D/peter.log.ReflectClass: reflectPrivateMethod string = I am declaredMethod 1 !
    08-27 15:11:38.004 11987-11987/com.android.peter.reflectdemo D/peter.log.ReflectDemo:  zenmode = 0
    

    总结
    本文列举了反射机制使用过程中常用的、重要的一些类及其方法,更多信息和用法需要近一步的阅读Google提供的相关文档和示例。

    在阅读Class类文档时发现一个特点,以通过反射获得Method对象为例,一般会提供四种方法,getMethod(parameterTypes)、getMethods()、getDeclaredMethod(parameterTypes)和getDeclaredMethods()。getMethod(parameterTypes)用来获取某个公有的方法的对象,getMethods()获得该类所有公有的方法,getDeclaredMethod(parameterTypes)获得该类某个方法,getDeclaredMethods()获得该类所有方法。带有Declared修饰的方法可以反射到私有的方法,没有Declared修饰的只能用来反射公有的方法。其他的Annotation、Field、Constructor也是如此。

    在ReflectClass类中还提供了两种反射PowerManager.shutdown()的方法,在调用的时候会输出如下log,提示没有相关权限。之前在项目中尝试反射其他方法的时候还遇到过有权限和没权限返回的值不一样的情况。如果源码中明确进行了权限验证,而你的应用又无法获得这个权限的话,建议就不要浪费时间反射了。

     W/System.err: java.lang.reflect.InvocationTargetException
     W/System.err:     at java.lang.reflect.Method.invoke(Native Method)
     W/System.err:     at .ReflectClass.shutDown(ReflectClass.java:104)
     W/System.err:     at .MainActivity$1.onClick(MainActivity.java:25)
     W/System.err:     at android.view.View.performClick(View.java:6259)
     W/System.err:     at android.view.View$PerformClick.run(View.java:24732)
     W/System.err:     at android.os.Handler.handleCallback(Handler.java:789)
     W/System.err:     at android.os.Handler.dispatchMessage(Handler.java:98)
     W/System.err:     at android.os.Looper.loop(Looper.java:164)
     W/System.err:     at android.app.ActivityThread.main(ActivityThread.java:6592)
     W/System.err:     at java.lang.reflect.Method.invoke(Native Method)
     W/System.err:     at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
     W/System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:769)
     W/System.err: Caused by: java.lang.SecurityException: Neither user 10224 nor current process has android.permission.REBOOT.
     W/System.err:     at android.os.Parcel.readException(Parcel.java:1942)
     W/System.err:     at android.os.Parcel.readException(Parcel.java:1888)
     W/System.err:     at android.os.IPowerManager$Stub$Proxy.shutdown(IPowerManager.java:787)
     W/System.err:  ... 12 more
    
    展开全文
  • [Java高级特性详解]

    千次阅读 2017-12-31 13:57:57
    Java高级特性之泛型 概述 举个栗子 特性 泛型类的使用 1 单泛型效果图 2 多泛型效果图 泛型方法的使用 1 泛型方法反编译效果图 泛型接口的使用了解 Java高级特性之枚举 举个栗子 枚举原理反编译枚举类 ...

    Java高级特性之泛型

    1. 概述

    泛型,即参数化类型。将类型由原来确定的类型参数定义如方法中的不确定的变量参数,此时类型可以称之为**(类型形参),然后在使用/调用时传入具体的类型**类型实参

    泛型的本质是: 允许在定义类、接口、方法时使用<T>类型形参,这个<T>类型形参将在声明变量、创建对象、调用方法时动态地指定(即传入实际的类型参数,也可称为类型实参),这就是Java泛型的泛型类泛型接口泛型方法,犹如一个在不同环境下都一样八面玲珑的变色龙。

    2. 举个栗子

    List arrayList = new ArrayList();
    arrayList.add("aaaa");
    arrayList.add(100);
    
    for(int i = 0; i< arrayList.size();i++){
        String item = (String)arrayList.get(i);
        Log.d("泛型测试","item = " + item);
    }
    
    毫无疑问,程序的运行结果会以崩溃结束:
    
    java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
    

    ArrayList可以存放任意类型,**例子中添加了一个String类型,添加了一个Integer类型,再使用时都以String的方式使用,因此程序崩溃了。**为了解决类似这样的问题(在编译阶段就可以解决),泛型应运而生。

    我们将第一行声明初始化list的代码更改一下,编译器会在编译阶段就能够帮我们发现类似这样的问题。

    List<String> arrayList = new ArrayList<String>();
    ...
    //arrayList.add(100); 在编译阶段,编译器就会报错
    

    3. 特性

    泛型只在编译阶段有效。看下面的代码:

    List<String> stringArrayList = new ArrayList<String>();
    List<Integer> integerArrayList = new ArrayList<Integer>();
    
    Class classStringArrayList = stringArrayList.getClass();
    Class classIntegerArrayList = integerArrayList.getClass();
    
    if(classStringArrayList.equals(classIntegerArrayList)){
        Log.d("泛型测试","类型相同");
    }
    
    输出结果:D/泛型测试: 类型相同。
    

    (重点): 通过上面的例子可以证明,在编译之后程序会采取去泛型化的措施。也就是说Java中的泛型,只在编译阶段有效。在编译过程中,正确检验泛型结果后,会将泛型的相关信息擦除,并且在对象进入和离开方法的边界处添加类型检查和类型转换的方法。也就是说,泛型信息不会进入到运行时阶段

    .java文件中是有泛型信息 ,但 Javac工具编译.java文件成 .class文件时已擦除了泛型相关信息,用类型检查和类型转换的代码代替了泛型)。

    总结成一句话泛型类型在逻辑上看以看成是多个的类型实际上都是相同的基本类型,参数化类型的本质决定了不同类型参数组成的参数化类型之间可以进行强制任意类型转换,但这些转换代码不用自己实现由Javac转换完成~


    4. 泛型类的使用

    泛型类型用于类的定义中,被称为泛型类。通过泛型可以完成对一组类的操作对外开放相同的接口。最典型的就是各种容器类,如:List、Set、Map。

    泛型类的最基本写法(在下面的例子中详解,建议Copy到开发工具):

    一个最普通的泛型类:
    
    //此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
    //在实例化泛型类时,必须指定T的具体类型
    public class Generic<T>{ 
        //key这个成员变量的类型为T,T的类型由外部指定  
        private T key;
       
    
        public Generic(T key) { //泛型构造方法形参key的类型也为T,T的类型由外部指定
            this.key = key;
        }
    
        public T getKey(){ //泛型方法getKey的返回值类型为T,T的类型由外部指定
            return key;
        }
    }
    //泛型的类型参数只能是类类型(包括自定义类),不能是简单类型
    //传入的实参类型需与泛型的类型参数类型相同,即为Integer.
    Generic<Integer> genericInteger = new Generic<Integer>(123456);
    
    //传入的实参类型需与泛型的类型参数类型相同,即为String.
    Generic<String> genericString = new Generic<String>("key_vlaue");
    Log.d("泛型测试","key is " + genericInteger.getKey());
    Log.d("泛型测试","key is " + genericString.getKey());
    
    泛型测试: key is 123456
    泛型测试: key is key_vlaue
    

    定义的泛型类,就一定要传入泛型类型实参么 ? 并不是这样,在使用泛型的时候如果传入泛型实参,则会根据传入的泛型实参做相应的限制,此时泛型才会起到本应起到的限制作用。 如果不传入泛型类型实参的话,在泛型类中使用泛型的方法或成员变量定义的类型可以为任何的类型

    看一个例子:
    
    Generic generic = new Generic("111111");
    Generic generic1 = new Generic(4444);
    Generic generic2 = new Generic(55.55);
    Generic generic3 = new Generic(false);
    
    Log.d("泛型测试","key is " + generic.getKey());
    Log.d("泛型测试","key is " + generic1.getKey());
    Log.d("泛型测试","key is " + generic2.getKey());
    Log.d("泛型测试","key is " + generic3.getKey());
    
    D/泛型测试: key is 111111
    D/泛型测试: key is 4444
    D/泛型测试: key is 55.55
    D/泛型测试: key is false
    

    注意
    泛型的类型参数只能是类类型不能是简单类型。不能对确切的泛型类型使用instanceof操作。如下面的操作是非法的,编译时会出错。

    if(ex_num instanceof Generic<Number>){   
    } 
    

    4.1 单泛型效果图!!!

    此处输入图片的描述

    4.2 多泛型效果图!!!

    此处输入图片的描述


    泛型应用的注意事项:

    其一、在定义泛型时,还可以用&来指定多个边界,如<V extends Serializable & cloneable> void method(){}

    其二、普通方法、构造方法和静态方法中都可以使用泛型;注意在构造方法中使用泛型如果有参数

    其三、可以用类型变量表示异常,称之为参数化的异常,可用于方法的throws列表中,但不能用于catch字句中;

    其四、在泛型中可同时有多个类型参数,在定义它们的<>中用逗号分开

    5. 泛型方法的使用

    在java中,泛型类的定义非常简单,但是泛型方法就比较复杂了。
    尤其是我们见到的大多数泛型类中的成员方法也都使用了泛型,有的甚至泛型类中也包含着泛型方法,这样在初学者中非常容易将泛型方法理解错了。
    泛型类,是在实例化类的时候指明泛型的具体类型;泛型方法,是在调用方法的时候指明泛型的具体类型 。

    /**
     * 泛型方法的基本介绍
     * @param tClass 传入的泛型实参
     * @return T 返回值为T类型
     * 说明:
     *  1)public 与  返回值中间<T>非常重要,可以理解为声明此
     *     方法为泛型方法, 事实上 <T> (<String>,<?> 等) 就是用来规定范性入参,返回. 可以不带,
     *   就是不限制,使用泛型方法时,至少返回值或参数有一个是泛型定义的,
     * 	 而且应该保持一致,否则可能会受到各种限制,
     *  
     *  2)只有声明了<T>的方法才是泛型方法,泛型类中的使用
     *     了泛型的成员方法并不是泛型方法。
     *
     * 3)<T> 表明该方法将使用泛型类型T,此时才可以在方法中使
     *     用泛型类型T。
     *
     *  4)与泛型类的定义一样,此处T可以随便写为任意标识,
     *      常见的如T、E、K、V等形式的参数常用于表示泛型。
     */
    
    //带*号必须遵守规则
    class Generatorfunction{
    
        //[修饰符] [是否静态]   [*返回值]    [*参数列表]
        public     static      <T>void      tell(T t){
            System.out.println(t);
        }
    
        public     static      <T>T         talk(T t){
            return t;
        }
    
        //泛型数组的使用 : 在调用方法的时候指明泛型的具体类型
        public  static       <T>void      array(T arr[]){
            for (int i = 0; i <arr.length ; i++) {
                System.out.println(arr[i]);
            }
        }
    }
    
    
    public class Generator {
        public static void main(String[] args) {
            /*
              泛型类,是在实例化类的时候指明泛型的具体类型;
              泛型方法,是在调用方法的时候指明泛型的具体类型
              泛型在Javac工具编译时会被识别为具体类型!!!
              */
    
            Generatorfunction s1   = new Generatorfunction();
            /*调用方法*/
            s1.tell(12.31);
    
            /*调用方法*/
            System.out.println(s1.talk("Hello Java泛型"));
    
            /*调用泛型数组方法*/
            Integer arr1[] ={11,12,13};
            s1.array(arr1);
    
            /*调用泛型数组方法*/
            String arr2[] ={"Hello","泛型","数组"};
            s1.array(arr2);
        }
    

    图有一个错误 如泛型在Javac工具编译时会被识别为具体类型!!!此处输入图片的描述

    5.1 泛型方法反编译效果图!!!

    此处输入图片的描述

    6. 泛型接口的使用(了解)

    泛型接口与泛型类的定义及使用基本相同。泛型接口常被用在各种类的生产器中,可以看一个例子:
    
    //定义一个泛型接口
    public interface Generator<T> {
        public T next();
    }
    
    当实现泛型接口的类,未传入泛型实参时:
    
    /**
     * 未传入泛型实参时,与泛型类的定义相同,在声明类的时候,
     * 需将泛型的声明也一起加到类中
     * 即:class FruitGenerator<T> implements Generator<T>{
     * 如果不声明泛型,如:class FruitGenerator implements   
     * Generator<T>,编译器会报错:"Unknown class"
     */
    class FruitGenerator<T> implements Generator<T>{
        @Override
        public T next() {
            return null;
        }
    }
    
    当实现泛型接口的类,传入泛型实参时:
    
    /**
     * 传入泛型实参时:
     * 定义一个生产器实现这个接口,虽然我们只创建了一个泛型接口Generator<T>
     * 但是我们可以为T传入无数个实参,形成无数种类型的Generator接口。
     * 在实现类实现泛型接口时,如已将泛型类型传入实参类型,则所有使用泛型的地方都要替换成传入的实参类型
     * 即:Generator<T>,public T next();中的的T都要替换成传入的String类型。
     */
    public class FruitGenerator implements Generator<String> {
    
        private String[] fruits = new String[]{"Apple", "Banana", "Pear"};
    
        @Override
        public String next() {
            Random rand = new Random();
            return fruits[rand.nextInt(3)];
        }
    }
    

    Java高级特性之枚举

    枚举类型是Java 5中新增特性的一部分,它是一种特殊的数据类型,之所以特殊是因为它既是一种类(class)类型却又比类类型多了些特殊的约束,但是这些约束的存在也造就了枚举类型的简洁性、安全性以及便捷性。下面先来看看什么是枚举?如何定义枚举?

    /**
     * 回忆一下下面的程序,这是在没有枚举类型时"定义常量常见的方式" ~
     *
     * 使用普通方式定义日期常量
     */
    public class DayDemo {
    
        public static final int MONDAY =1;
    
        public static final int TUESDAY=2;
    
        public static final int WEDNESDAY=3;
    
        public static final int THURSDAY=4;
    
        public static final int FRIDAY=5;
    
        public static final int SATURDAY=6;
    
        public static final int SUNDAY=7;
    
    }
    
    

    1. 举个栗子

    上述的常量定义常量的方式称为int枚举模式,这样的定义方式并没有什么错,但它存在许多不足,如在类型安全和使用方便性上并没有多少好处,如果存在定义int值相同的变量,混淆的几率还是很大的,编译器也不会提出任何警告,因此这种方式在枚举出现后并不提倡,现在我们利用枚举类型来重新定义上述的常量,同时也感受一把枚举定义的方式,如下定义周一到周日的常量

    //枚举类型,使用关键字enum
    enum Day {
        MONDAY, TUESDAY, WEDNESDAY,
        THURSDAY, FRIDAY, SATURDAY, SUNDAY
    }
    

    相当简洁,在定义枚举类型时我们使用的关键字是enum,与class关键字类似,只不过前者是定义枚举类型,后者是定义类类型。枚举类型Day中分别定义了从周一到周日的值,这里要注意,值一般是大写的字母,多个值之间以逗号分隔。同时我们应该知道的是枚举类型可以像类(class)类型定义为单独的文件,当然也可以定义在其他类内部,更重要的是枚举常量在类型安全性和便捷性都很有保证,如果出现类型问题编译器也会提示我们改进,但务必记住枚举表示的类型其取值是必须有限的,也就是说每个值都是可以枚举出来的,比如上述描述的一周共有七天。那么该如何使用呢?如下:

    public class EnumDemo {
    
        public static void main(String[] args){
            //直接引用
            Day day =Day.MONDAY;
        }
    
    }
    //定义枚举类型
    enum Day {
        MONDAY, TUESDAY, WEDNESDAY,
        THURSDAY, FRIDAY, SATURDAY, SUNDAY
    }
    

    就像上述代码那样,直接引用枚举的值即可,这便是枚举类型的最简单Demo

    2. 枚举原理(反编译枚举类)

    我们大概了解了枚举类型的定义与简单使用后,现在有必要来了解一下枚举类型的基本实现原理。实际上在使用关键字enum创建枚举类型并编译后,编译器会为我们生成一个相关的类,这个类继承了Java API中的java.lang.Enum类,也就是说通过关键字enum创建枚举类型在编译后 ,该类应该继承自java.lang.Enum类。下面我们编译前面定义的EnumDemo.java并查看生成的class文件来验证这个结论:

    //查看目录下的java文件
    zejian@zejiandeMBP enumdemo$ ls
    EnumDemo.java
    //利用javac命令编译EnumDemo.java
    zejian@zejiandeMBP enumdemo$ javac EnumDemo.java 
    //查看生成的class文件,注意有Day.class和EnumDemo.class 两个
    zejian@zejiandeMBP enumdemo$ ls
    Day.class  EnumDemo.class  EnumDemo.java
    

    利用javac编译前面定义的EnumDemo.java文件后分别生成了Day.class和EnumDemo.class文件,而Day.class就是枚举类型,这也就验证前面所说的使用关键字enum定义枚举类型并编译后,编译器会自动帮助我们生成一个与枚举相关的类。我们再来看看反编译Day.class文件:

    //反编译Day.class 后,星号是重点!!!
    final class Day extends Enum //Javac工具编译时自动继承 Enum抽象类 *
    {
        //编译器为我们添加的静态的values()方法 ,获取全部枚举 *
        public static Day[] values()
        {
            return (Day[])$VALUES.clone();
        }
      //编译器添加的静态的valueOf()方法,间接调用了Enum类的valueOf方法 
        public static Day valueOf(String s)
        {
            return (Day)Enum.valueOf(com/zejian/enumdemo/Day, s);
        }
        //私有构造方法 重点!!! * 
        private Day(String s, int i)
        {
            super(s, i);
        }
         //前面定义的7种枚举实例 *
        public static final Day MONDAY;
        public static final Day TUESDAY;
        public static final Day WEDNESDAY;
        public static final Day THURSDAY;
        public static final Day FRIDAY;
        public static final Day SATURDAY;
        public static final Day SUNDAY;
        private static final Day $VALUES[];
    
        static 
        {    
            //实例化枚举实例 *
            MONDAY = new Day("MONDAY", 0);
            TUESDAY = new Day("TUESDAY", 1);
            WEDNESDAY = new Day("WEDNESDAY", 2);
            THURSDAY = new Day("THURSDAY", 3);
            FRIDAY = new Day("FRIDAY", 4);
            SATURDAY = new Day("SATURDAY", 5);
            SUNDAY = new Day("SUNDAY", 6);
            $VALUES = (new Day[] {
            
    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
            });
        }
    }
    

    从反编译的代码可以看出编译器确实帮助我们生成了一个Day类(注意该类是final类型的,将无法被继承)而且该类继承自java.lang.Enum类该类是一个抽象类(稍后我们会分析该类中的主要方法),除此之外,编译器还帮助我们生成了7个Day类型的实例对象分别对应枚举中定义的7个日期,这也充分说明了我们前面使用关键字enum定义的Day类型中的每种日期枚举常量也是实实在在的Day实例对象,只不过代表的内容不一样而已。注意编译器还为我们生成了两个静态方法,分别是values()和 valueOf(),稍后会分析它们的用法,到此我们也就明白了,使用关键字enum定义的枚举类型,在编译期后,也将转换成为一个实实在在的类,而在该类中会存在每个在枚举类型中定义好变量的对应实例对象,如上述的MONDAY枚举类型对应public static final Day MONDAY ;,同时编译器会为该类创建两个方法,分别为values()和valueOf()。到此相信我们对枚举的实现原理也比较清晰,下面我们深入了解一下java.lang.Enum类以及values()和valueOf()的用途


    注意(重点) : 反编译枚举源码后显示此类并无独有的构造方法与Get方法,类加载时只会调用它的父类Enum类的构造方法,也可自定义构造方法与Get方法来实现枚举进阶使用后面有介绍,请好好观察反编译源码! ! !


    3. Enum抽象类常见方法

    Enum是所有 Java 语言枚举类型的公共父类(注意Enum是抽象类),以下是它的常见方法:

    返回值类型 方法名称 方法说明
    int compareTo(E o) 比较此枚举与指定对象的顺序
    boolean equals(Object other) 当指定对象等于此枚举常量时返回true。
    Class<?> getDeclaringClass() 返回与此枚举常量的枚举类型相对应的 Class 对象
    String name() 返回此枚举常量的名称,在其枚举声明中对其进行声明
    int ordinal() 返回枚举常量的序数(它在枚举声明中的位置,其中初始常量序数为零)
    String toString() 返回枚举常量的名称,它包含在声明中
    static<T extends Enum<T>> T static valueOf(Class enumType, String name) 返回带指定名称的指定枚举类型的枚举常量。

    这里主要说明一下 ordinal()方法该方法获取的是枚举变量在枚举类中声明的顺序,下标从0开始,如日期中的MONDAY在第一个位置,那么MONDAY的ordinal值就是0,如果MONDAY的声明位置发生变化,那么ordinal方法获取到的值也随之变化.注意在大多数情况下我们都不应该首先使用该方法,毕竟它总是变幻莫测的。compareTo(E o)方法 则是比较枚举的大小,注意其内部实现是根据每个枚举的ordinal值大小进行比较的。name()方法与toString()几乎是等同的,都是输出变量的字符串形式。 至于valueOf(Class enumType, String name)方法 则是根据枚举类的Class对象和枚举名称获取枚举常量,注意该方法是静态的, 后面在枚举单例时,我们还会详细分析该方法,下面的代码演示了上述方法:

    package com.zejian.enumdemo;
    
    public class EnumDemo {
    
        public static void main(String[] args){
    
            //创建枚举数组
      Day[] days=new Day[]{Day.MONDAY, Day.TUESDAY, Day.WEDNESDAY,
          Day.THURSDAY, Day.FRIDAY, Day.SATURDAY, Day.SUNDAY};
    
            for (int i = 0; i <days.length ; i++) {
                System.out.println("day["+i+"].ordinal():"+days[i].ordinal());
            }
    
            System.out.println("-------------------------------------");
            //通过compareTo方法比较,实际上其内部是通过ordinal()值比较的
            System.out.println("days[0].compareTo(days[1]):"+days[0].compareTo(days[1]));
            System.out.println("days[0].compareTo(days[1]):"+days[0].compareTo(days[2]));
    
            //获取该枚举对象的Class对象引用,当然也可以通过getClass方法
            Class<?> clazz = days[0].getDeclaringClass();
            System.out.println("clazz:"+clazz);
    
            System.out.println("-------------------------------------");
    
            //name()
            System.out.println("days[0].name():"+days[0].name());
            System.out.println("days[1].name():"+days[1].name());
            System.out.println("days[2].name():"+days[2].name());
            System.out.println("days[3].name():"+days[3].name());
    
            System.out.println("-------------------------------------");
    
            System.out.println("days[0].toString():"+days[0].toString());
            System.out.println("days[1].toString():"+days[1].toString());
            System.out.println("days[2].toString():"+days[2].toString());
            System.out.println("days[3].toString():"+days[3].toString());
    
            System.out.println("-------------------------------------");
    
            Day d=Enum.valueOf(Day.class,days[0].name());
            Day d2=Day.valueOf(Day.class,days[0].name());
            System.out.println("d:"+d);
            System.out.println("d2:"+d2);
        }
     /**
     执行结果:
       day[0].ordinal():0
       day[1].ordinal():1
       day[2].ordinal():2
       day[3].ordinal():3
       day[4].ordinal():4
       day[5].ordinal():5
       day[6].ordinal():6
       -------------------------------------
       days[0].compareTo(days[1]):-1
       days[0].compareTo(days[1]):-2
       clazz:class com.zejian.enumdemo.Day
       -------------------------------------
       days[0].name():MONDAY
       days[1].name():TUESDAY
       days[2].name():WEDNESDAY
       days[3].name():THURSDAY
       -------------------------------------
       days[0].toString():MONDAY
       days[1].toString():TUESDAY
       days[2].toString():WEDNESDAY
       days[3].toString():THURSDAY
       -------------------------------------
       d:MONDAY
       d2:MONDAY
       */
    
    }
    enum Day {
        MONDAY, TUESDAY, WEDNESDAY,
        THURSDAY, FRIDAY, SATURDAY, SUNDAY
    }
    

    3.1 Enum枚举类构造函数讲解(图文并茂)

    此处输入图片的描述

    到此对于抽象类Enum类的基本内容就介绍完了,这里提醒大家一点,Enum类内部会有一个构造函数,该构造函数只能有编译器调用,我们是无法手动操作的,不妨看看Enum类的主要源码

    //实现了Comparable
    public abstract class Enum<E extends Enum<E>>
    //Enum类为抽象类重点看 * !!!
            implements Comparable<E>, Serializable {
    
        private final String name; //枚举字符串名称
    
        public final String name() {
            return name;
        }
    
        private final int ordinal;//枚举顺序值 * 
    
        public final int ordinal() {
            return ordinal;
        }
    
        //枚举的构造方法,只能由编译器调用 *
        protected Enum(String name, int ordinal) {
            this.name = name;
            this.ordinal = ordinal;
        }
    
        public String toString() {
            return name;
        }
    
        public final boolean equals(Object other) {
            return this==other;
        }
    
        //比较的是ordinal值
        public final int compareTo(E o) {
            Enum<?> other = (Enum<?>)o;
            Enum<E> self = this;
    if (self.getClass() != other.getClass() && // optimization
     self.getDeclaringClass() != other.getDeclaringClass())
                throw new ClassCastException();
            return self.ordinal - other.ordinal;//根据ordinal值比较大小
        }
    
        @SuppressWarnings("unchecked")
        public final Class<E> getDeclaringClass() {
            //获取class对象引用,getClass()是Object的方法
            Class<?> clazz = getClass();
            //获取父类Class对象引用
            Class<?> zuper = clazz.getSuperclass();
            return (zuper == Enum.class) ? (Class<E>)clazz : (Class<E>)zuper;
        }
    
        public static <T extends Enum<T>> T valueOf(Class<T> enumType,
                                                    String name) {
            //enumType.enumConstantDirectory()获取到的是一个map集合,key值就是name值,value则是枚举变量值   
            //enumConstantDirectory是class对象内部的方法,根据class对象获取一个map集合的值       
            T result = enumType.enumConstantDirectory().get(name);
            if (result != null)
                return result;
            if (name == null)
                throw new NullPointerException("Name is null");
            throw new IllegalArgumentException(
                "No enum constant " + enumType.getCanonicalName() + "." + name);
        }
    
        //.....省略其他没用的方法
    }
    

    通过Enum源码,可以知道Enum实现了Comparable接口,这也是可以使用compareTo比较的原因,当然Enum构造函数也是存在的,该函数只能由编译器调用,毕竟我们只能使用enum关键字定义枚举,其他事情就放心交给编译器吧,可以看看上面的Gif图中演示过!!!

    //由编译器调用
    protected Enum(String name, int ordinal) {
            this.name = name;
            this.ordinal = ordinal;
        }
    

    编译器生成的Values()方法与ValueOf()方法

    values()方法和valueOf(String name)方法是编译器生成的static方法,因此从前面的分析中,在Enum类中并没出现values()方法但valueOf()方法还是有出现的,只不过编译器生成的valueOf()方法需传递一个name参数,而Enum自带的静态方法valueOf()则需要传递两个方法,从前面反编译后的代码可以看出,编译器生成的valueOf方法最终还是调用了Enum类的valueOf方法(看上方Gif图Debug演示),下面通过代码来演示这两个方法的作用:

    Day[] days2 = Day.values();
    System.out.println("day2:"+Arrays.toString(days2));
    Day day = Day.valueOf("MONDAY");
    System.out.println("day:"+day);
    
    /**
     输出结果:
     day2:[MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY]
     day:MONDAY
     */
    

    从结果可知道,values()方法的作用就是获取枚举类中的所有变量,并作为数组返回,valueOf(String name)方法与Enum类中的valueOf方法的作用类似根据名称获取枚举变量,只不过编译器生成的valueOf方法更简洁些只需传递一个参数。这里我们还必须注意到,由于values()方法是由编译器插入到枚举类中的static方法,所以如果我们将枚举实例向上转型为Enum,那么values()方法将无法被调用,因为Enum类中并没有values()方法valueOf()方法也是同样的道理,注意是一个参数的。

     //正常使用
    Day[] ds=Day.values();
    //向上转型Enum
    Enum e = Day.MONDAY;
    //无法调用,没有此方法
    //e.values();
    

    枚举与Class对象

    上述我们提到当枚举实例向上转型为Enum类型后,values()方法将会失效,也就无法一次性获取所有枚举实例变量,但是由于Class对象的存在,即使不使用values()方法,还是有可能一次获取到所有枚举实例变量的,在Class对象中存在如下方法:

    返回类型 方法名称 方法说明
    T[] getEnumConstants() 返回该枚举类型的所有元素,如果Class对象不是枚举类型,则返回null。
    boolean isEnum() 当且仅当该类声明为源代码中的枚举时返回 true
    因此通过getEnumConstants()方法,同样可以轻而易举地获取所有枚举实例变量下面通过代码来演示这个功能:

    //正常使用
    Day[] ds=Day.values();
    //向上转型Enum
    Enum e = Day.MONDAY;
    //无法调用,没有此方法
    //e.values();
    //获取class对象引用
    Class<?> clasz = e.getDeclaringClass();
    if(clasz.isEnum()) {
        Day[] dsz = (Day[]) clasz.getEnumConstants();
        System.out.println("dsz:"+Arrays.toString(dsz));
    }
    
    /**
       输出结果:
       dsz:[MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY]
     */
    

    正如上述代码所展示,通过Enum的class对象的getEnumConstants方法,我们仍能一次性获取所有的枚举实例常量。


    4. 枚举的进阶用法(前面是基础,开发中一般不常用)

    在前面的分析中,我们都是基于简单枚举类型的定义,也就是在定义枚举时只定义了枚举实例类型并没定义方法或者成员变量,实际上使用关键字enum定义的枚举类,除了不能使用继承(因为编译器会自动为我们继承Enum抽象类而Java只支持单继承,因此枚举类是无法手动实现继承的),可以把enum类当成常规类,也就是说我们可以向enum类中添加方法和变量,甚至是mian方法,下面就来感受一把。


    4.1 向枚举类添加自定义的构造函数与Get方法(重点)

    Enum类添加方法自定义构造函数 ! ! !

    重新定义一个日期枚举类,带有desc成员变量描述该日期的对于中文描述,同时定义一个getDesc方法返回中文描述内容自定义私有构造函数,在声明枚举实例时传入对应的中文描述,代码如下:

    package com.zejian.enumdemo;
    
    
    public enum Day2 {
        MONDAY("星期一"),
        TUESDAY("星期二"),
        WEDNESDAY("星期三"),
        THURSDAY("星期四"),
        FRIDAY("星期五"),
        SATURDAY("星期六"),
        SUNDAY("星期日");//记住要用分号结束
    
        private String desc;//中文描述
    
        /**
         * 私有构造,防止被外部调用
         * @param desc
         */
        private Day2(String desc){
            this.desc=desc;
        }
    
        /**
         * 定义方法,返回描述,跟常规类的定义没区别
         * @return
         */
        public String getDesc(){
            return desc;
        }
    
        public static void main(String[] args){
            for (Day2 day:Day2.values()) {
                System.out.println("name:"+day.name()+
                        ",desc:"+day.getDesc());
            }//* 重点
              System.out.println(Day2.TUESDAY.getDesc);
        }
    }
        /**
         输出结果:
         name:MONDAY,desc:星期一
         name:TUESDAY,desc:星期二
         name:WEDNESDAY,desc:星期三
         name:THURSDAY,desc:星期四
         name:FRIDAY,desc:星期五
         name:SATURDAY,desc:星期六
         name:SUNDAY,desc:星期日
         */
    
    

    结合:枚举2 反编译后的代码来观察学习(从枚举2截的图 )!!!


    此处输入图片的描述

    自定义一个进阶枚举类实现 ;


    此处输入图片的描述

    也可以看看下面这个例子

    public  enum RespondCode {
       //声明枚举变量隐式于public static final RespondCode类SUCCESS引用指向了在static{ SUCCESS=new RespondCode(0,"SUCCESS") } *
       
       //0 成功Code
        SUCCESS(0,"SUCCESS"),
        //1 错误Code
        ERORR(1,"ERORR"),
        //2 非法参数Code
        ILLEGAL_ARGUMENT(2,"ILLEGAL_ARGUMENT"),
        //10 未登录Code
        NEED_LOGIN(10,"NEED_LOGIN");
    
        ArrayList ArrayLists= new ArrayList<String>();
        //只提供Get方法不可在运行时修改原值
       private final  int code;
       private final  String desc;
    
       //反编译后发现枚举常量如SUCCESS相当于的public static final RespondCode 对象的类引用,后面的参数则是向构造方法里传参~ *
        RespondCode(int code, String desc) {
            this.code = code;
            this.desc = desc;
        }
        public int getCode() {
            return code;
        }
        public String getDesc() {
            return desc;
        }
    }
    public static void main(String[] args) {
          //相当于RespondCode类中SUCCESS 静态类的getCode方法 *
            RespondCode.SUCCESS.getCode();
        /*
         * 控制台输出: 0
         */
           
        }
    

    从上述代码可知,在enum类中确实可以像定义常规类一样声明变量或者成员方法。但是我们必须注意到,如果打算在enum类中定义方法,务必在声明完枚举实例后使用分号分开,倘若在枚举实例前定义任何方法,编译器都将会报错,无法编译通过,同时即使自定义了构造函数且enum的定义结束,我们也永远无法手动调用构造函数创建枚举实例,毕竟这事只能由编译器执行。

    4.2 关于覆盖enum类方法(了解)

    既然enum类跟常规类的定义没什么区别(实际上enum还是有些约束的),那么覆盖父类的方法也不会是什么难说,可惜的是父类Enum中的定义的方法只有toString方法没有使用final修饰,因此只能覆盖toString方法,如下通过覆盖toString省去了getDesc方法:

    package com.zejian.enumdemo;
    
    /**
     * Created by zejian on 2017/5/8.
     * Blog : http://blog.csdn.net/javazejian [原文地址,请尊重原创]
     */
    public enum Day2 {
        MONDAY("星期一"),
        TUESDAY("星期二"),
        WEDNESDAY("星期三"),
        THURSDAY("星期四"),
        FRIDAY("星期五"),
        SATURDAY("星期六"),
        SUNDAY("星期日");//记住要用分号结束
    
        private String desc;//中文描述
    
        /**
         * 私有构造,防止被外部调用
         * @param desc
         */
        private Day2(String desc){
            this.desc=desc;
        }
    
        /**
         * 覆盖
         * @return
         */
        @Override
        public String toString() {
            return desc;
        }
    
    
        public static void main(String[] args){
            for (Day2 day:Day2.values()) {
                System.out.println("name:"+day.name()+
                        ",desc:"+day.toString());
            }
        }
    
        /**
         输出结果:
         name:MONDAY,desc:星期一
         name:TUESDAY,desc:星期二
         name:WEDNESDAY,desc:星期三
         name:THURSDAY,desc:星期四
         name:FRIDAY,desc:星期五
         name:SATURDAY,desc:星期六
         name:SUNDAY,desc:星期日
         */
    }
    

    enum类中定义抽象方法

    与常规抽象类一样,enum类允许我们为其定义抽象方法,然后使每个枚举实例都实现该方法,以便产生不同的行为方式,注意abstract关键字对于枚举类来说并不是必须的如下:

    package com.zejian.enumdemo;
    
    /**
     * Created by zejian on 2017/5/9.
     * Blog : http://blog.csdn.net/javazejian [原文地址,请尊重原创]
     */
    public enum EnumDemo3 {
    
        FIRST{
            @Override
            public String getInfo() {
                return "FIRST TIME";
            }
        },
        SECOND{
            @Override
            public String getInfo() {
                return "SECOND TIME";
            }
        }
    
        ;
    
        /**
         * 定义抽象方法
         * @return
         */
        public abstract String getInfo();
    
        //测试
        public static void main(String[] args){
            System.out.println("F:"+EnumDemo3.FIRST.getInfo());
            System.out.println("S:"+EnumDemo3.SECOND.getInfo());
            /**
             输出结果:
             F:FIRST TIME
             S:SECOND TIME
             */
        }
    }
    

    通过这种方式就可以轻而易举地定义每个枚举实例的不同行为方式。我们可能注意到,enum类的实例似乎表现出了多态的特性,可惜的是枚举类型的实例终究不能作为类型传递使用,就像下面的使用方式,编译器是不可能答应的:

    //无法通过编译,毕竟EnumDemo3.FIRST是个实例对象
     public void text(EnumDemo3.FIRST instance){ }
    
    在枚举实例常量中定义抽象方法
    
    enum类与接口
    
    由于Java单继承的原因,enum类并不能再继承其它类,但并不妨碍它实现接口,因此enum类同样是可以实现多接口的,如下:
    
    package com.zejian.enumdemo;
    
    /**
     * Created by zejian on 2017/5/8.
     * Blog : http://blog.csdn.net/javazejian [原文地址,请尊重原创]
     */
    
    interface food{
        void eat();
    }
    
    interface sport{
        void run();
    }
    
    public enum EnumDemo2 implements food ,sport{
        FOOD,
        SPORT,
        ; //分号分隔
    
        @Override
        public void eat() {
            System.out.println("eat.....");
        }
    
        @Override
        public void run() {
            System.out.println("run.....");
        }
    }
    
    有时候,我们可能需要对一组数据进行分类,比如进行食物菜单分类而且希望这些菜单都属于food类型,appetizer(开胃菜)、mainCourse(主菜)、dessert(点心)、Coffee等,每种分类下有多种具体的菜式或食品,此时可以利用接口来组织,如下(代码引用自Thinking in Java):
    
    public interface Food {
      enum Appetizer implements Food {
        SALAD, SOUP, SPRING_ROLLS;
      }
      enum MainCourse implements Food {
        LASAGNE, BURRITO, PAD_THAI,
        LENTILS, HUMMOUS, VINDALOO;
      }
      enum Dessert implements Food {
        TIRAMISU, GELATO, BLACK_FOREST_CAKE,
        FRUIT, CREME_CARAMEL;
      }
      enum Coffee implements Food {
        BLACK_COFFEE, DECAF_COFFEE, ESPRESSO,
        LATTE, CAPPUCCINO, TEA, HERB_TEA;
      }
    }
    
    public class TypeOfFood {
      public static void main(String[] args) {
        Food food = Appetizer.SALAD;
        food = MainCourse.LASAGNE;
        food = Dessert.GELATO;
        food = Coffee.CAPPUCCINO;
      }
    } 
    
    通过这种方式可以很方便组织上述的情景,同时确保每种具体类型的食物也属于Food,现在我们利用一个枚举嵌套枚举的方式,把前面定义的菜谱存放到一个Meal菜单中,通过这种方式就可以统一管理菜单的数据了。
    
    public enum Meal{
      APPETIZER(Food.Appetizer.class),
      MAINCOURSE(Food.MainCourse.class),
      DESSERT(Food.Dessert.class),
      COFFEE(Food.Coffee.class);
      private Food[] values;
      private Meal(Class<? extends Food> kind) {
        //通过class对象获取枚举实例
        values = kind.getEnumConstants();
      }
      public interface Food {
        enum Appetizer implements Food {
          SALAD, SOUP, SPRING_ROLLS;
        }
        enum MainCourse implements Food {
          LASAGNE, BURRITO, PAD_THAI,
          LENTILS, HUMMOUS, VINDALOO;
        }
        enum Dessert implements Food {
          TIRAMISU, GELATO, BLACK_FOREST_CAKE,
          FRUIT, CREME_CARAMEL;
        }
        enum Coffee implements Food {
          BLACK_COFFEE, DECAF_COFFEE, ESPRESSO,
          LATTE, CAPPUCCINO, TEA, HERB_TEA;
        }
      }
    
    枚举与switch
    
    关于枚举与switch是个比较简单的话题,使用switch进行条件判断时,条件参数一般只能是整型,字符型。而枚举型确实也被switch所支持,在java 1.7后switch也对字符串进行了支持。这里我们简单看一下switch与枚举类型的使用:
    
    
    /**
     * Created by zejian on 2017/5/9.
     * Blog : http://blog.csdn.net/javazejian [原文地址,请尊重原创]
     */
    
    enum Color {GREEN,RED,BLUE}
    
    public class EnumDemo4 {
    
        public static void printName(Color color){
            switch (color){
                case BLUE: //无需使用Color进行引用
                    System.out.println("蓝色");
                    break;
                case RED:
                    System.out.println("红色");
                    break;
                case GREEN:
                    System.out.println("绿色");
                    break;
            }
        }
    
        public static void main(String[] args){
            printName(Color.BLUE);
            printName(Color.RED);
            printName(Color.GREEN);
    
            //蓝色
            //红色
            //绿色
        }
    }
    
    
    需要注意的是使用在于switch条件进行结合使用时,无需使用Color引用。
    
    枚举与单例模式
    
    单例模式可以说是最常使用的设计模式了,它的作用是确保某个类只有一个实例,自行实例化并向整个系统提供这个实例。在实际应用中,线程池、缓存、日志对象、对话框对象常被设计成单例,总之,选择单例模式就是为了避免不一致状态,下面我们将会简单说明单例模式的几种主要编写方式,从而对比出使用枚举实现单例模式的优点。首先看看饿汉式的单例模式:
    
    /**
     * Created by wuzejian on 2017/5/9.
     * 饿汉式(基于classloder机制避免了多线程的同步问题)
     */
    public class SingletonHungry {
    
        private static SingletonHungry instance = new SingletonHungry();
    
        private SingletonHungry() {
        }
    
        public static SingletonHungry getInstance() {
            return instance;
        }
    }
    
    
    显然这种写法比较简单,但问题是无法做到延迟创建对象,事实上如果该单例类涉及资源较多,创建比较耗时间时,我们更希望它可以尽可能地延迟加载,从而减小初始化的负载,于是便有了如下的懒汉式单例:
    
    /**
     * Created by wuzejian on 2017/5/9..
     * 懒汉式单例模式(适合多线程安全)
     */
    public class SingletonLazy {
    
        private static volatile SingletonLazy instance;
    
        private SingletonLazy() {
        }
    
        public static synchronized SingletonLazy getInstance() {
            if (instance == null) {
                instance = new SingletonLazy();
            }
            return instance;
        }
    }
    

    这种写法能够在多线程中很好的工作避免同步问题,同时也具备lazy loading机制,遗憾的是,由于synchronized的存在,效率很低,在单线程的情景下,完全可以去掉synchronized,为了兼顾效率与性能问题,改进后代码如下:

    public class Singleton {
    private static volatile Singleton singleton = null;

    private Singleton(){}
    
    public static Singleton getSingleton(){
        if(singleton == null){
            synchronized (Singleton.class){
                if(singleton == null){
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }    
    

    }

    这种编写方式被称为“双重检查锁”,主要在getSingleton()方法中,进行两次null检查。这样可以极大提升并发度,进而提升性能。毕竟在单例中new的情况非常少,绝大多数都是可以并行的读操作,因此在加锁前多进行一次null检查就可以减少绝大多数的加锁操作,也就提高了执行效率。但是必须注意的是volatile关键字,该关键字有两层语义。第一层语义是可见性,可见性是指在一个线程中对该变量的修改会马上由工作内存(Work Memory)写回主内存(Main Memory),所以其它线程会马上读取到已修改的值,关于工作内存和主内存可简单理解为高速缓存(直接与CPU打交道)和主存(日常所说的内存条),注意工作内存是线程独享的,主存是线程共享的。volatile的第二层语义是禁止指令重排序优化,我们写的代码(特别是多线程代码),由于编译器优化,在实际执行的时候可能与我们编写的顺序不同。编译器只保证程序执行结果与源代码相同,却不保证实际指令的顺序与源代码相同,这在单线程并没什么问题,然而一旦引入多线程环境,这种乱序就可能导致严重问题。volatile关键字就可以从语义上解决这个问题,值得关注的是volatile的禁止指令重排序优化功能在Java 1.5后才得以实现,因此1.5前的版本仍然是不安全的,即使使用了volatile关键字。或许我们可以利用静态内部类来实现更安全的机制,静态内部类单例模式如下:

    /**

    • Created by wuzejian on 2017/5/9.

    • 静态内部类
      */
      public class SingletonInner {
      private static class Holder {
      private static SingletonInner singleton = new SingletonInner();
      }

      private SingletonInner(){}

      public static SingletonInner getSingleton(){
      return Holder.singleton;
      }
      }

    正如上述代码所展示的,我们把Singleton实例放到一个静态内部类中,这样可以避免了静态实例在Singleton类的加载阶段(类加载过程的其中一个阶段的,此时只创建了Class对象,关于Class对象可以看博主另外一篇博文, 深入理解Java类型信息(Class对象)与反射机制)就创建对象,毕竟静态变量初始化是在SingletonInner类初始化时触发的,并且由于静态内部类只会被加载一次,所以这种写法也是线程安全的。从上述4种单例模式的写法中,似乎也解决了效率与懒加载的问题,但是它们都有两个共同的缺点:

    序列化可能会破坏单例模式,比较每次反序列化一个序列化的对象实例时都会创建一个新的实例,解决方案如下:

    //测试例子(四种写解决方式雷同)
    public class Singleton implements java.io.Serializable {
    public static Singleton INSTANCE = new Singleton();

    protected Singleton() {
    }

    //反序列时直接返回当前INSTANCE
    private Object readResolve() {
    return INSTANCE;
    }
    }

    使用反射强行调用私有构造器,解决方式可以修改构造器,让它在创建第二个实例的时候抛异常,如下:

    public static Singleton INSTANCE = new Singleton();
    private static volatile boolean flag = true;
    private Singleton(){
    if(flag){
    flag = false;
    }else{
    throw new RuntimeException(“The instance already exists !”);
    }
    }

    如上所述,问题确实也得到了解决,但问题是我们为此付出了不少努力,即添加了不少代码,还应该注意到如果单例类维持了其他对象的状态时还需要使他们成为transient的对象,这种就更复杂了,那有没有更简单更高效的呢?当然是有的,那就是枚举单例了,先来看看如何实现:

    /**

    • Created by wuzejian on 2017/5/9.
    • 枚举单利
      */
      public enum SingletonEnum {
      INSTANCE;
      private String name;
      public String getName(){
      return name;
      }
      public void setName(String name){
      this.name = name;
      }
      }
    展开全文
  • import java.util.ArrayList; /********** End **********/ public class HelloWorld { @SuppressWarnings("unchecked") public ArrayList getList() { /********** Begin **********/

    第1关 集合的基本使用

    package step1;
    // 导包
    /********** Begin **********/
    import java.util.ArrayList;				
    /********** End **********/
    						
    public class HelloWorld {
        
    	@SuppressWarnings("unchecked") 
    	public ArrayList getList() {
    		/********** Begin **********/
    		ArrayList List=new ArrayList();
    				List.add("https:www.educoder.net");
    				List.add(2018.423);
    				return List;
    		
    		
    		
    		
    		/********** End **********/
    	}
    	
    }
    

    第2关 ArrayList集合的增删改查

    package step2;
    
    import java.util.ArrayList;
    import java.util.Scanner;
    
    public class HelloWorld {
    	
        @SuppressWarnings("unchecked")
    	public static void main(String[] args) {
    		//获取输入的数据并添加至集合
    		Scanner sc = new Scanner(System.in);
    		ArrayList list = new ArrayList<>();
            int length = sc.nextInt();
    		for(int i =0 ; i< length; i++){
    			list.add(sc.next());
    		}
    		/********** Begin *********/
    		list.remove(list.size()-1);
            list.remove(0);
            
            list.add("hello");
            list.add("educoder");
            list.set(2,"list");
    
    		int size=list.size();
            for(int i=0;i<size;i++){
                System.out.println(list.get(i));
            }
    	
    		/********** End **********/
    	}
    }
    

    第3关 集合的体系结构

    package step3;
    
    import java.util.ArrayList;
    import java.util.HashSet;
    import java.util.Set;
    import java.util.HashMap;
    import java.util.LinkedList;
    import java.util.Map;
    
    public class HelloWorld {
    	
    	public HashSet getHashSet(){
    		/********** Begin **********/
    		HashSet hs=new HashSet();
            hs.add("www.educoder.net");
    		
    		return hs;
    		
    		
    		/********** End **********/
    	}
    	
    	public ArrayList getArrayList(){
    		/********** Begin **********/
    	    ArrayList al  =new ArrayList();
            al.add("www.educoder.net");
            return al;
    		
    		
    		
    		
    		/********** End **********/
    	}
    	
    	public LinkedList getLinkedList(){
    		/********** Begin **********/
    		LinkedList ll=new LinkedList();
            ll.add("www.educoder.net");
    
            return ll;
    		
    		
    		
    		
    		/********** End **********/
    	}
    	
    	public Map getHashMap(){
    		/********** Begin **********/
    		HashMap hm=new HashMap();
            hm.put("address","www.educoder.net");
    
            return hm;
    		
    		/********** End **********/
    	}	
    }
    

    第4关 泛型

    package step4;
    
    import java.util.*;
    
    public class HelloWorld {
    	
    	
    	public static void main(String[] args) {
    		Scanner sc = new Scanner(System.in);
    		//程序会输入三次数据
    		/********** Begin **********/
    	    List<String>  list=new ArrayList<String>();
           
            for(int i=0;i<3;i++){
                list.add(sc.next());
            }
    
            System.out.println("集合的第1个数据为:"+list.get(0));
    		System.out.println("集合的第2个数据为:"+list.get(1));
    		System.out.println("集合的第3个数据为:"+list.get(2));
    	
    		/********** End **********/
    	}
    }
    

    第5关 Map集合的增删改查

    package step5;
    
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Scanner;
    
    public class HelloWorld {
    
    	public static void main(String[] args) {
    		Map<String, Integer> menuDict = new HashMap<>();
    		Scanner sc = new Scanner(System.in);
    		for (int i = 0; i < 5; i++) {
    			menuDict.put(sc.next(),sc.nextInt());
    		}
    		/********** Begin **********/
    		menuDict.put("lamb",50);
            System.out.println(menuDict.get("fish"));
    		menuDict.put("fish",100);
            menuDict.remove("noodles");
             System.out.println(menuDict.toString());
    
    		/********** End **********/
    	}
    	
    }
    

    第6关 选择题

    C

    ----刘向阳啊

    展开全文
  • Java学习之java高级特性

    万次阅读 多人点赞 2018-04-13 11:11:30
    下面是个人的总结 一、集合框架及泛型1、集合框架是一套性能优良、使用方便的接口和类(位于java.util包中)解决数组在存储上不能很好适应元素数量动态变化,查找效率低的缺陷集合接口: Map、Collection(子接口List...
  • JAVA高级特性总结

    千次阅读 2013-11-28 11:26:41
    1.File类可以完成对文件、目录的操作,新建、删除、重命名文件、目录等,但不能访问文件本身的内容,访问文件本身内容需要使用输入输出流,对于Java来说文件和目录都是一个概念。 2.Window的路径分隔符使用反斜线...
  • Educoder–Java高级特性(第一章)- IO流【笔记+参考代码】 第一关 1、下列关于字节和字符的说法正确的是(BC) A、字节 = 字符 + 编码 B、字符 = 字节 + 编码 C、字节 = 字符 + 解码 D、字符 = 字节 + 解码 2、...
  • Educoder–Java高级特性(第二章)- 集合框架【笔记+参考代码】 第一关 编程要求 请仔细阅读右侧代码,根据方法内的提示,在Begin - End区域内进行代码补充,创建ArrayList集合并且向集合中添加数据,具体要求...
  • public static void main(String[] args) { // <? extends Animal>限定了泛型类只能是Animal或其子类对象的List List<? extends Animal> animals = new ArrayList<... //从集合中获取对象是是可以的,...
  • Educoder–Java高级特性(第三章)- Java 字符串与集合练习——词频统计【笔记+参考代码】 第一关 编程要求 请仔细阅读右侧代码,根据方法内的提示,在Begin - End区域内进行代码补充,具体任务如下: 用String....
  • Educoder–Java高级特性(第五、六章)- 多线程练习题+Java反射【笔记+参考代码】 第一关 编程要求 请仔细阅读右侧代码,在 Begin-End 区域内进行代码补充,使线程依照先后顺序依次输出JavaThread+线程名。 提示...
  • Educoder–Java高级特性(第九章)- JDBC(上)【笔记+参考代码】 第一关 编程要求 在右侧编辑器补充代码,完成下列相应任务: 加载数据库驱动;【平台数据库连接的用户(user)为root,密码(password)为123123...
  • Java高级特性之泛型

    千次阅读 2015-12-31 16:48:59
    如果你对Java三大特性中的多态性理解的比较透彻的话,泛型就比较好理解了。多态性表示一个对象具备多种状态。比如说你自己,你首先是一个人,同时你在看这篇帖子说明你是一个程序员,下了班之后你可能有变成了禽兽。...
  • Java高级特性之反射

    千次阅读 2016-01-06 09:59:09
    老规矩我们还是先提出几个问题,一门技术必然要能解决一定的问题,才有去...换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或
  • Educoder–Java高级特性(第四章)- 多线程基础(1)使用线程【笔记+参考代码】 第一关 编程要求 请仔细阅读右侧代码,根据方法内的提示,在Begin - End区域内进行代码补充,具体任务如下: 使用继承Thread类的...
  • Java高级特性之枚举

    千次阅读 2015-12-31 10:45:31
    Java SE5之前,我们要使用枚举类型时,通常会使用static final 定义一组int常量来标识,代码如下public static final int MAN = 0; public static final int WOMAN = 1;相信很多小伙伴,在实际开发中也是那么干的...
  • Educoder–Java高级特性(第四章)- 多线程基础(3)线程同步【笔记+参考代码】 第一关 1.在并发编程中,我们需要以下哪几个特性来保持多线程程序执行正确( ABD ) A、可见性 B、原子性 C、并发性 D、有序性 2.请...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 172,119
精华内容 68,847
关键字:

java高级特性

java 订阅