傳智播客 2015 劉意 Java基礎-視頻-筆記day27(完結)(2016年5月1日12:42:20)


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的新特性,讓我歡喜讓我憂。
(出處: 傳智播客論壇_傳智播客旗下社區)
================================================
Java 8或許是迄今為止最令人期待的Java版本,最初定於2013年的9月份發布,
        但由於一系列的安全漏洞問題,最終在2014年的3月18日才發布了正式版。
        作為同門兄弟,NetBeans 8.0跟JDK 8同時發布,在版本號上也與JDK保持一致。
        看來,關鍵時刻還得靠兄弟啊。呵呵。
        下載鏈接:
 
        那么,在經過這么長的時間閉關修煉后,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(了解)
        可以去網上了解資料
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM