1. 程式人生 > 實用技巧 >最好用的流程編輯器bpmn-js系列之基本使用

最好用的流程編輯器bpmn-js系列之基本使用

Java反射機制

目錄

概要

概要

1、執行緒這塊還有那些內容呢?列舉一下

	1.1、守護執行緒

		java語言中執行緒分為兩大類:
			一類是:使用者執行緒
			一類是:守護執行緒(後臺執行緒)
			其中具有代表性的就是:垃圾回收執行緒(守護執行緒)。

		守護執行緒的特點:
			一般守護執行緒是一個死迴圈,所有的使用者執行緒只要結束,
			守護執行緒自動結束。
		
		注意:主執行緒main方法是一個使用者執行緒。

		守護執行緒用在什麼地方呢?
			每天00:00的時候系統資料自動備份。
			這個需要使用到定時器,並且我們可以將定時器設定為守護執行緒。
			一直在那裡看著,沒到00:00的時候就備份一次。所有的使用者執行緒
			如果結束了,守護執行緒自動退出,沒有必要進行資料備份了。

	1.2、定時器
		定時器的作用:
			間隔特定的時間,執行特定的程式。

			每週要進行銀行賬戶的總賬操作。
			每天要進行資料的備份操作。

			在實際的開發中,每隔多久執行一段特定的程式,這種需求是很常見的,
			那麼在java中其實可以採用多種方式實現:
				
				可以使用sleep方法,睡眠,設定睡眠時間,沒到這個時間點醒來,執行
				任務。這種方式是最原始的定時器。(比較low)

				在java的類庫中已經寫好了一個定時器:java.util.Timer,可以直接拿來用。
				不過,這種方式在目前的開發中也很少用,因為現在有很多高階框架都是支援
				定時任務的。

				在實際的開發中,目前使用較多的是Spring框架中提供的SpringTask框架,
				這個框架只要進行簡單的配置,就可以完成定時器的任務。


	1.3、實現執行緒的第三種方式:實現Callable介面。(JDK8新特性。)
		這種方式實現的執行緒可以獲取執行緒的返回值。
		之前講解的那兩種方式是無法獲取執行緒返回值的,因為run方法返回void。

		思考:
			系統委派一個執行緒去執行一個任務,該執行緒執行完任務之後,可能
			會有一個執行結果,我們怎麼能拿到這個執行結果呢?
				使用第三種方式:實現Callable介面方式。


	1.4、關於Object類中的wait和notify方法。(生產者和消費者模式!)

		第一:wait和notify方法不是執行緒物件的方法,是java中任何一個java物件
		都有的方法,因為這兩個方式是Object類中自帶的。
			wait方法和notify方法不是通過執行緒物件呼叫,
			不是這樣的:t.wait(),也不是這樣的:t.notify()..不對。
		
		第二:wait()方法作用?
			Object o = new Object();
			o.wait();

			表示:
				讓正在o物件上活動的執行緒進入等待狀態,無期限等待,
				直到被喚醒為止。
				o.wait();方法的呼叫,會讓“當前執行緒(正在o物件上
				活動的執行緒)”進入等待狀態。

		第三:notify()方法作用?
			Object o = new Object();
			o.notify();

			表示:
				喚醒正在o物件上等待的執行緒。
			
			還有一個notifyAll()方法:
				這個方法是喚醒o物件上處於等待的所有執行緒。


2、反射機制(比較簡單,因為只要會查幫助文件,就可以了。)
	
	2.1、反射機制有什麼用?
		通過java語言中的反射機制可以操作位元組碼檔案。
		優點類似於黑客。(可以讀和修改位元組碼檔案。)
		通過反射機制可以操作程式碼片段。(class檔案。)
	
	2.2、反射機制的相關類在哪個包下?
		java.lang.reflect.*;
	
	2.3、反射機制相關的重要的類有哪些?

		java.lang.Class:代表整個位元組碼,代表一個型別,代表整個類。

		java.lang.reflect.Method:代表位元組碼中的方法位元組碼。代表類中的方法。

		java.lang.reflect.Constructor:代表位元組碼中的構造方法位元組碼。代表類中的構造方法

		java.lang.reflect.Field:代表位元組碼中的屬性位元組碼。代表類中的成員變數(靜態變數+例項變數)。

		java.lang.Class:
			public class User{
				// Field
				int no;

				// Constructor
				public User(){
				
				}
				public User(int no){
					this.no = no;
				}

				// Method
				public void setNo(int no){
					this.no = no;
				}
				public int getNo(){
					return no;
				}
			}

3、關於JDK中自帶的類載入器:(聊一聊,不需要掌握,知道當然最好!)
	3.1、什麼是類載入器?
		專門負責載入類的命令/工具。
		ClassLoader
	
	3.2、JDK中自帶了3個類載入器
		啟動類載入器:rt.jar
		擴充套件類載入器:ext/*.jar
		應用類載入器:classpath
	
	3.3、假設有這樣一段程式碼:
		String s = "abc";
		
		程式碼在開始執行之前,會將所需要類全部載入到JVM當中。
		通過類載入器載入,看到以上程式碼類載入器會找String.class
		檔案,找到就載入,那麼是怎麼進行載入的呢?

			首先通過“啟動類載入器”載入。
				注意:啟動類載入器專門載入:C:\Program Files\Java\jdk1.8.0_101\jre\lib\rt.jar
				rt.jar中都是JDK最核心的類庫。
			
			如果通過“啟動類載入器”載入不到的時候,
			會通過"擴充套件類載入器"載入。
				注意:擴充套件類載入器專門載入:C:\Program Files\Java\jdk1.8.0_101\jre\lib\ext\*.jar

	
			如果“擴充套件類載入器”沒有載入到,那麼
			會通過“應用類載入器”載入。
				注意:應用類載入器專門載入:classpath中的類。
	
	3.4、java中為了保證類載入的安全,使用了雙親委派機制。
		優先從啟動類載入器中載入,這個稱為“父”
		“父”無法載入到,再從擴充套件類載入器中載入,
		這個稱為“母”。雙親委派。如果都載入不到,
		才會考慮從應用類載入器中載入。直到載入
		到為止。

總結:

1、回顧反射機制

	1.1、什麼是反射機制?反射機制有什麼用?
		反射機制:可以操作位元組碼檔案
		作用:可以讓程式更加靈活。

	1.2、反射機制相關的類在哪個包下?
		java.lang.reflect.*;

	1.3、反射機制相關的主要的類?
		java.lang.Class
		java.lang.reflect.Method;
		java.lang.reflect.Constructor;
		java.lang.reflect.Field;

	1.4、在java中獲取Class的三種方式?
		第一種:	 
			Class c = Class.forName("完整類名");
		第二種:
			Class c = 物件.getClass();
		第三種:
			Class c = int.class;
			Class c = String.class;

	1.5、獲取了Class之後,可以呼叫無引數構造方法來例項化物件

		//c代表的就是日期Date型別
		Class c = Class.forName("java.util.Date");

		//例項化一個Date日期型別的物件
		Object obj = c.newInstance();

		一定要注意:
			newInstance()底層呼叫的是該型別的無引數構造方法。
			如果沒有這個無引數構造方法會出現"例項化"異常。
	
	1.6、如果你只想讓一個類的“靜態程式碼塊”執行的話,你可以怎麼做?
		Class.forName("該類的類名");
		這樣類就載入,類載入的時候,靜態程式碼塊執行!!!!
		在這裡,對該方法的返回值不感興趣,主要是為了使用“類載入”這個動作。
	
	1.7、關於路徑問題?

		String path = Thread.currentThread().getContextClassLoader()
						  .getResource("寫相對路徑,但是這個相對路徑從src出發開始找").getPath();	

		String path = Thread.currentThread().getContextClassLoader()
						  .getResource("abc").getPath();	//必須保證src下有abc檔案。

		String path = Thread.currentThread().getContextClassLoader()
						  .getResource("a/db").getPath();	//必須保證src下有a目錄,a目錄下有db檔案。
		
		String path = Thread.currentThread().getContextClassLoader()
						  .getResource("com/example/test.properties").getPath();	
						  //必須保證src下有com目錄,com目錄下有example目錄。
						  //example目錄下有test.properties檔案。

		這種方式是為了獲取一個檔案的絕對路徑。(通用方式,不會受到環境移植的影響。)
		但是該檔案要求放在類路徑下,換句話說:也就是放到src下面。
		src下是類的根路徑。

		直接以流的形式返回:
		InputStream in = Thread.currentThread().getContextClassLoader()
								.getResourceAsStream("com/example/test.properties");

	1.8、IO + Properties,怎麼快速繫結屬性資原始檔?

		//要求:第一這個檔案必須在類路徑下
		//第二這個檔案必須是以.properties結尾。
		ResourceBundle bundle = ResourceBundle.getBundle("com/example/test");
		String value = bundle.getString(key);
	

2、今日反射機制的重點內容
	2.1、通過反射機制訪問物件的某個屬性。
	2.2、通過反射機制呼叫物件的某個方法。
	2.3、通過反射機制呼叫某個構造方法例項化物件。
	2.4、通過反射機制獲取父類以及父型別介面。

ReflectTest01——獲取類的位元組碼檔案

怎麼獲取java.lang.Class例項?有三種方式:

  1. Class c = Class.forName("完整類名帶包名");

    靜態方法
    方法的引數是一個字串。
    字串需要的是一個完整類名。
    完整類名必須帶有包名。java.lang包也不能省略。

  2. Class c = 物件.getClass();

    Class c1 = Class.forName("java.lang.String"); // c1代表String.class檔案,或者說c1代表String型別。

    String s = "abc";

    Class x = s.getClass(); // x代表String.class位元組碼檔案,x代表String型別。

    System.out.println(c1 == x); // true(==判斷的是物件的記憶體地址。)

    位元組碼記憶體圖:

  3. Class c = 任何型別.class;

    Class z = String.class; // z代表String型別
    Class k = Date.class; // k代表Date型別
    Class f = int.class; // f代表int型別
    Class e = double.class; // e代表double型別

import java.util.Date;

/*
要操作一個類的位元組碼,需要首先獲取到這個類的位元組碼,怎麼獲取java.lang.Class例項?
    三種方式
        第一種:Class c = Class.forName("完整類名帶包名");
        第二種:Class c = 物件.getClass();
        第三種:Class c = 任何型別.class;

 */
public class ReflectTest01 {
    public static void main(String[] args) {
        /*
        Class.forName()
            1、靜態方法
            2、方法的引數是一個字串。
            3、字串需要的是一個完整類名。
            4、完整類名必須帶有包名。java.lang包也不能省略。
         */
        Class c1 = null;
        Class c2 = null;
        try {
            c1 = Class.forName("java.lang.String"); // c1代表String.class檔案,或者說c1代表String型別。
            c2 = Class.forName("java.util.Date"); // c2代表Date型別
            Class c3 = Class.forName("java.lang.Integer"); // c3代表Integer型別
            Class c4 = Class.forName("java.lang.System"); // c4代表System型別
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        // java中任何一個物件都有一個方法:getClass()
        String s = "abc";
        Class x = s.getClass(); // x代表String.class位元組碼檔案,x代表String型別。
        System.out.println(c1 == x); // true(==判斷的是物件的記憶體地址。)

        Date time = new Date();
        Class y = time.getClass();
        System.out.println(c2 == y); // true (c2和y兩個變數中儲存的記憶體地址都是一樣的,都指向方法區中的位元組碼檔案。)

        // 第三種方式,java語言中任何一種型別,包括基本資料型別,它都有.class屬性。
        Class z = String.class; // z代表String型別
        Class k = Date.class; // k代表Date型別
        Class f = int.class; // f代表int型別
        Class e = double.class; // e代表double型別

        System.out.println(x == z); // true

    }
}

ReflectTest02——通過Class的newInstance()方法來例項化物件

獲取到Class,能幹什麼?

可以通過Class的newInstance()方法來例項化物件。

注意:newInstance()方法內部實際上呼叫了無引數構造方法,必須保證無參構造存在才可以

例如:

Class c = Class.forName("com.example.java.bean.User"); // c代表User型別。

Object obj = c.newInstance();

public class ReflectTest02 {
    public static void main(String[] args) {

        // 這是不使用反射機制,建立物件
        User user = new User();
        System.out.println(user);

        // 下面這段程式碼是以反射機制的方式建立物件。
        try {
            // 通過反射機制,獲取Class,通過Class來例項化物件
            Class c = Class.forName("com.example.java.bean.User"); // c代表User型別。

            // newInstance() 這個方法會呼叫User這個類的無引數構造方法,完成物件的建立。
            // 重點是:newInstance()呼叫的是無參構造,必須保證無參構造是存在的!
            Object obj = c.newInstance();

            System.out.println(obj); // com.example.java.bean.User@10f87f48
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
    }
}

public class User {
    public User(){
        System.out.println("無引數構造方法!");
    }

    // 定義了有引數的構造方法,無引數構造方法就沒了。
    public User(String s){

    }
}

ReflectTest03——驗證反射機制的靈活性

為什麼說反射機制靈活?

Java程式碼寫一遍,再不改變java原始碼的基礎之上,可以做到不同物件的例項化。
非常之靈活。(符合OCP開閉原則:對擴充套件開放,對修改關閉。)

反射機制的用途是什麼?

高階框架底層使用反射機制比較多。

學會了反射機制有利於你理解剖析框架底層的原始碼。

/*
驗證反射機制的靈活性。
    java程式碼寫一遍,再不改變java原始碼的基礎之上,可以做到不同物件的例項化。
    非常之靈活。(符合OCP開閉原則:對擴充套件開放,對修改關閉。)

後期你們要學習的是高階框架,而工作過程中,也都是使用高階框架,
包括: ssh ssm
    Spring SpringMVC MyBatis
    Spring Struts Hibernate
    ...
    這些高階框架底層實現原理:都採用了反射機制。所以反射機制還是重要的。
    學會了反射機制有利於你理解剖析框架底層的原始碼。
 */
public class ReflectTest03 {
    public static void main(String[] args) throws Exception{

        // 這種方式程式碼就寫死了。只能建立一個User型別的物件
        //User user = new User();

        // 以下程式碼是靈活的,程式碼不需要改動,可以修改配置檔案,配置檔案修改之後,可以創建出不同的例項物件。
        // 通過IO流讀取classinfo.properties檔案
        FileReader reader = new FileReader("chapter25/classinfo2.properties");
        // 建立屬性類物件Map
        Properties pro = new Properties(); // key value都是String
        // 載入
        pro.load(reader);
        // 關閉流
        reader.close();

        // 通過key獲取value
        String className = pro.getProperty("className");
        //System.out.println(className);

        // 通過反射機制例項化物件
        Class c = Class.forName(className);
        Object obj = c.newInstance();
        System.out.println(obj);
    }
}

ReflectTest04——Class.forName()只是讓一個類的靜態程式碼塊執行,其它程式碼一律不執行

/*
研究一下:Class.forName()發生了什麼?
    記住,重點:
        如果你只是希望一個類的靜態程式碼塊執行,其它程式碼一律不執行,
        你可以使用:
            Class.forName("完整類名");
        這個方法的執行會導致類載入,類載入時,靜態程式碼塊執行。

提示:
    後面JDBC技術的時候我們還需要。
 */
public class ReflectTest04 {
    public static void main(String[] args) {
        try {
            // Class.forName()這個方法的執行會導致:類載入。
            Class.forName("com.example.reflect.MyClass");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

public class MyClass {

    // 靜態程式碼塊在類載入時執行,並且只執行一次。
    static {
        System.out.println("MyClass類的靜態程式碼塊執行了!");
    }
}

AboutPath——怎麼獲取一個檔案的絕對路徑

為什麼不使用FileReader reader = new FileReader("chapter25/classinfo2.properties");這種方式獲取檔案路徑的缺點?

這種方式的路徑缺點是:移植性差,在IDEA中預設的當前路徑是project的根。

這個程式碼假設離開了IDEA,換到了其它位置,可能當前路徑就不是project的根了,這時這個路徑就無效了。

怎麼獲取一個檔案的絕對路徑?

String path = Thread.currentThread().getContextClassLoader().getResource("classinfo2.properties").getPath(); // 這種方式獲取檔案絕對路徑是通用的。

前提是:檔案需要在類路徑下。才能用這種方式。

在src下的都是類路徑下。

public class AboutPath {
    public static void main(String[] args) throws Exception{
        // 這種方式的路徑缺點是:移植性差,在IDEA中預設的當前路徑是project的根。
        // 這個程式碼假設離開了IDEA,換到了其它位置,可能當前路徑就不是project的根了,這時這個路徑就無效了。
        //FileReader reader = new FileReader("chapter25/classinfo2.properties");

        // 接下來說一種比較通用的一種路徑。即使程式碼換位置了,這樣編寫仍然是通用的。
        // 注意:使用以下通用方式的前提是:這個檔案必須在類路徑下。
        // 什麼類路徑下?方式在src下的都是類路徑下。【記住它】
        // src是類的根路徑。
        /*
        解釋:
            Thread.currentThread() 當前執行緒物件
            getContextClassLoader() 是執行緒物件的方法,可以獲取到當前執行緒的類載入器物件。
            getResource() 【獲取資源】這是類載入器物件的方法,當前執行緒的類載入器預設從類的根路徑下載入資源。
         */
        String path = Thread.currentThread().getContextClassLoader()
                .getResource("classinfo2.properties").getPath(); // 這種方式獲取檔案絕對路徑是通用的。

        // 採用以上的程式碼可以拿到一個檔案的絕對路徑。
        // /C:/Users/Administrator/IdeaProjects/javase/out/production/chapter25/classinfo2.properties
        System.out.println(path);

        // 獲取db.properties檔案的絕對路徑(從類的根路徑下作為起點開始)
        String path2 = Thread.currentThread().getContextClassLoader()
                .getResource("com/example/java/bean/db.properties").getPath();
        System.out.println(path2);

    }
}

IoPropertiesTest——以流的形式返回讀取的檔案

public class IoPropertiesTest {
    public static void main(String[] args) throws Exception{

        // 獲取一個檔案的絕對路徑了!!!!!
        /*String path = Thread.currentThread().getContextClassLoader()
                .getResource("classinfo2.properties").getPath();
        FileReader reader = new FileReader(path);*/

        // 直接以流的形式返回。
        InputStream reader = Thread.currentThread().getContextClassLoader()
                .getResourceAsStream("classinfo2.properties");

        Properties pro = new Properties();
        pro.load(reader);
        reader.close();
        // 通過key獲取value
        String className = pro.getProperty("className");
        System.out.println(className);
    }
}

ResourceBundleTest——資源繫結器

import java.util.ResourceBundle;
/*
java.util包下提供了一個資源繫結器,便於獲取屬性配置檔案中的內容。
使用以下這種方式的時候,屬性配置檔案xxx.properties必須放到類路徑下。
 */
public class ResourceBundleTest {
    public static void main(String[] args) {

        // 資源繫結器,只能繫結xxx.properties檔案。並且這個檔案必須在類路徑下。副檔名也必須是properties
        // 並且在寫路徑的時候,路徑後面的副檔名不能寫。
        //ResourceBundle bundle = ResourceBundle.getBundle("classinfo2");

        ResourceBundle bundle = ResourceBundle.getBundle("com/example/java/bean/db");

        String className = bundle.getString("className");
        System.out.println(className);
    }
}

ReflectTest05——反射Student類獲得當中所有的Field

package com.example.java.reflect;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

/*
反射Student類當中所有的Field(瞭解一下)
 */
public class ReflectTest05 {
    public static void main(String[] args) throws Exception{

        // 獲取整個類
        Class studentClass = Class.forName("com.example.java.bean.Student");

        //com.example.java.bean.Student
        String className = studentClass.getName();
        System.out.println("完整類名:" + className);

        String simpleName = studentClass.getSimpleName();
        System.out.println("簡類名:" + simpleName);

        // 獲取類中所有的public修飾的Field
        Field[] fields = studentClass.getFields();
        System.out.println(fields.length); // 測試陣列中只有1個元素
        // 取出這個Field
        Field f = fields[0];
        // 取出這個Field它的名字
        String fieldName = f.getName();
        System.out.println(fieldName);

        // 獲取所有的Field
        Field[] fs = studentClass.getDeclaredFields();
        System.out.println(fs.length); // 4

        System.out.println("==================================");
        // 遍歷
        for(Field field : fs){
            // 獲取屬性的修飾符列表
            int i = field.getModifiers(); // 返回的修飾符是一個數字,每個數字是修飾符的代號!!!
            System.out.println(i);
            // 可以將這個“代號”數字轉換成“字串”嗎?
            String modifierString = Modifier.toString(i);
            System.out.println(modifierString);
            // 獲取屬性的型別
            Class fieldType = field.getType();
            //String fName = fieldType.getName();
            String fName = fieldType.getSimpleName();
            System.out.println(fName);
            // 獲取屬性的名字
            System.out.println(field.getName());
        }
    }
}

Student實體類:

package com.example.java.bean;

// 反射屬性Field
public class Student {

    // Field翻譯為欄位,其實就是屬性/成員
    // 4個Field,分別採用了不同的訪問控制權限修飾符
    private String name; // Field物件
    protected int age; // Field物件
    boolean sex;
    public int no;
    public static final double MATH_PI = 3.1415926;
}

ReflectTest06——通過反射機制,反編譯一個類的屬性Field

package com.example.java.reflect;

//通過反射機制,反編譯一個類的屬性Field(瞭解一下)

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

public class ReflectTest06 {
    public static void main(String[] args) throws Exception{

        // 建立這個是為了拼接字串。
        StringBuilder s = new StringBuilder();

        //Class studentClass = Class.forName("com.example.java.bean.Student");
        Class studentClass = Class.forName("java.lang.Thread");

        s.append(Modifier.toString(studentClass.getModifiers()) + " class " + studentClass.getSimpleName() + " {\n");

        Field[] fields = studentClass.getDeclaredFields();
        for(Field field : fields){
            s.append("\t");
            s.append(Modifier.toString(field.getModifiers()));
            s.append(" ");
            s.append(field.getType().getSimpleName());
            s.append(" ");
            s.append(field.getName());
            s.append(";\n");
        }

        s.append("}");
        System.out.println(s);

    }
}

ReflectTest07——通過反射機制設定和訪問一個java物件的屬性

package com.example.java.reflect;

import com.example.java.bean.Student;

import java.lang.reflect.Field;

/*
必須掌握:
    怎麼通過反射機制訪問一個java物件的屬性?
        給屬性賦值set
        獲取屬性的值get
 */
public class ReflectTest07 {
    public static void main(String[] args) throws Exception{

        // 我們不使用反射機制,怎麼去訪問一個物件的屬性呢?
        Student s = new Student();

        // 給屬性賦值
        s.no = 1111; //三要素:給s物件的no屬性賦值1111
                    //要素1:物件s
                    //要素2:no屬性
                    //要素3:1111

        // 讀屬性值
        // 兩個要素:獲取s物件的no屬性的值。
        System.out.println(s.no);

        // 使用反射機制,怎麼去訪問一個物件的屬性。(set get)
        Class studentClass = Class.forName("com.bjpowernode.java.bean.Student");
        Object obj = studentClass.newInstance(); // obj就是Student物件。(底層呼叫無引數構造方法)

        // 獲取no屬性(根據屬性的名稱來獲取Field)
        Field noFiled = studentClass.getDeclaredField("no");

        // 給obj物件(Student物件)的no屬性賦值
        /*
        雖然使用了反射機制,但是三要素還是缺一不可:
            要素1:obj物件
            要素2:no屬性
            要素3:2222值
        注意:反射機制讓程式碼複雜了,但是為了一個“靈活”,這也是值得的。
         */
        noFiled.set(obj, 22222); // 給obj物件的no屬性賦值2222

        // 讀取屬性的值
        // 兩個要素:獲取obj物件的no屬性的值。
        System.out.println(noFiled.get(obj));

        // 可以訪問私有的屬性嗎?
        Field nameField = studentClass.getDeclaredField("name");

        // 打破封裝(反射機制的缺點:打破封裝,可能會給不法分子留下機會!!!)
        // 這樣設定完之後,在外部也是可以訪問private的。
        nameField.setAccessible(true);

        // 給name屬性賦值
        nameField.set(obj, "jackson");
        // 獲取name屬性的值
        System.out.println(nameField.get(obj));
    }
}

ArgsTest——可變長度引數

package com.bjpowernode.java.reflect;
/*
可變長度引數
    int... args 這就是可變長度引數
    語法是:型別...  (注意:一定是3個點。)

    1、可變長度引數要求的引數個數是:0~N個。
    2、可變長度引數在引數列表中必須在最後一個位置上,而且可變長度引數只能有1個。
    3、可變長度引數可以當做一個數組來看待
 */
public class ArgsTest {
    public static void main(String[] args) {
        m();
        m(10);
        m(10, 20);

        // 編譯報錯
        //m("abc");

        m2(100);
        m2(200, "abc");
        m2(200, "abc", "def");
        m2(200, "abc", "def", "xyz");

        m3("ab", "de", "kk", "ff");

        String[] strs = {"a","b","c"};
        // 也可以傳1個數組
        m3(strs);

        // 直接傳1個數組
        m3(new String[]{"我","是","中","國", "人"}); //沒必要

        m3("我","是","中","國", "人");
    }

    public static void m(int... args){
        System.out.println("m方法執行了!");
    }

    //public static void m2(int... args2, String... args1){}

    // 必須在最後,只能有1個。
    public static void m2(int a, String... args1){

    }

    public static void m3(String... args){
        //args有length屬性,說明args是一個數組!
        // 可以將可變長度引數當做一個數組來看。
        for(int i = 0; i < args.length; i++){
            System.out.println(args[i]);
        }
    }
}

ReflectTest08——反射Method

package com.bjpowernode.java.reflect;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

/*
作為了解內容(不需要掌握):
    反射Method
 */
public class ReflectTest08 {
    public static void main(String[] args) throws Exception{

        // 獲取類了
        Class userServiceClass = Class.forName("com.bjpowernode.java.service.UserService");

        // 獲取所有的Method(包括私有的!)
        Method[] methods = userServiceClass.getDeclaredMethods();
        //System.out.println(methods.length); // 2

        // 遍歷Method
        for(Method method : methods){
            // 獲取修飾符列表
            System.out.println(Modifier.toString(method.getModifiers()));
            // 獲取方法的返回值型別
            System.out.println(method.getReturnType().getSimpleName());
            // 獲取方法名
            System.out.println(method.getName());
            // 方法的修飾符列表(一個方法的引數可能會有多個。)
            Class[] parameterTypes = method.getParameterTypes();
            for(Class parameterType : parameterTypes){
                System.out.println(parameterType.getSimpleName());
            }
        }
    }
}

UserService

package com.bjpowernode.java.service;

/**
 * 使用者業務類
 */
public class UserService {

    /**
     * 登入方法
     * @param name 使用者名稱
     * @param password 密碼
     * @return true表示登入成功,false表示登入失敗!
     */
    public boolean login(String name,String password){
        if("admin".equals(name) && "123".equals(password)){
            return true;
        }
        return false;
    }

    // 可能還有一個同名login方法
    // java中怎麼區分一個方法,依靠方法名和引數列表。
    public void login(int i){

    }

    /**
     * 退出系統的方法
     */
    public void logout(){
        System.out.println("系統已經安全退出!");
    }
}

ReflectTest09——反編譯一個類的方法

package com.bjpowernode.java.reflect;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

/*
瞭解一下,不需要掌握(反編譯一個類的方法。)
 */
public class ReflectTest09 {
    public static void main(String[] args) throws Exception{
        StringBuilder s = new StringBuilder();
        //Class userServiceClass = Class.forName("com.bjpowernode.java.service.UserService");
        Class userServiceClass = Class.forName("java.lang.String");
        s.append(Modifier.toString(userServiceClass.getModifiers()) + " class "+userServiceClass.getSimpleName()+" {\n");

        Method[] methods = userServiceClass.getDeclaredMethods();
        for(Method method : methods){
            //public boolean login(String name,String password){}
            s.append("\t");
            s.append(Modifier.toString(method.getModifiers()));
            s.append(" ");
            s.append(method.getReturnType().getSimpleName());
            s.append(" ");
            s.append(method.getName());
            s.append("(");
            // 引數列表
            Class[] parameterTypes = method.getParameterTypes();
            for(Class parameterType : parameterTypes){
                s.append(parameterType.getSimpleName());
                s.append(",");
            }
            // 刪除指定下標位置上的字元
            s.deleteCharAt(s.length() - 1);
            s.append("){}\n");
        }

        s.append("}");
        System.out.println(s);
    }
}

ReflectTest10——通過反射機制怎麼呼叫一個物件的方法

package com.bjpowernode.java.reflect;

import com.bjpowernode.java.service.UserService;

import java.lang.reflect.Method;

/*
重點:必須掌握,通過反射機制怎麼呼叫一個物件的方法?
    五顆星*****

    反射機制,讓程式碼很具有通用性,可變化的內容都是寫到配置檔案當中,
    將來修改配置檔案之後,建立的物件不一樣了,呼叫的方法也不同了,
    但是java程式碼不需要做任何改動。這就是反射機制的魅力。
 */
public class ReflectTest10 {
    public static void main(String[] args) throws Exception{
        // 不使用反射機制,怎麼呼叫方法
        // 建立物件
        UserService userService = new UserService();
        // 呼叫方法
        /*
        要素分析:
            要素1:物件userService
            要素2:login方法名
            要素3:實參列表
            要素4:返回值
         */
        boolean loginSuccess = userService.login("admin","123");
        //System.out.println(loginSuccess);
        System.out.println(loginSuccess ? "登入成功" : "登入失敗");

        // 使用反射機制來呼叫一個物件的方法該怎麼做?
        Class userServiceClass = Class.forName("com.bjpowernode.java.service.UserService");
        // 建立物件
        Object obj = userServiceClass.newInstance();
        // 獲取Method
        Method loginMethod = userServiceClass.getDeclaredMethod("login", String.class, String.class);
        //Method loginMethod = userServiceClass.getDeclaredMethod("login", int.class);
        // 呼叫方法
        // 呼叫方法有幾個要素? 也需要4要素。
        // 反射機制中最最最最最重要的一個方法,必須記住。
        /*
        四要素:
        loginMethod方法
        obj物件
        "admin","123" 實參
        retValue 返回值
         */
        Object retValue = loginMethod.invoke(obj, "admin","123123");
        System.out.println(retValue);
    }
}

ReflectTest11——反編譯一個類的Constructor構造方法

package com.bjpowernode.java.reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;

/*
反編譯一個類的Constructor構造方法。
 */
public class ReflectTest11 {
    public static void main(String[] args) throws Exception{
        StringBuilder s = new StringBuilder();
        Class vipClass = Class.forName("java.lang.String");
        s.append(Modifier.toString(vipClass.getModifiers()));
        s.append(" class ");
        s.append(vipClass.getSimpleName());
        s.append("{\n");

        // 拼接構造方法
        Constructor[] constructors = vipClass.getDeclaredConstructors();
        for(Constructor constructor : constructors){
            //public Vip(int no, String name, String birth, boolean sex) {
            s.append("\t");
            s.append(Modifier.toString(constructor.getModifiers()));
            s.append(" ");
            s.append(vipClass.getSimpleName());
            s.append("(");
            // 拼接引數
            Class[] parameterTypes = constructor.getParameterTypes();
            for(Class parameterType : parameterTypes){
                s.append(parameterType.getSimpleName());
                s.append(",");
            }
            // 刪除最後下標位置上的字元
            if(parameterTypes.length > 0){
                s.deleteCharAt(s.length() - 1);
            }
            s.append("){}\n");
        }

        s.append("}");
        System.out.println(s);
    }
}

ReflectTest12——通過反射機制呼叫構造方法例項化java物件

package com.bjpowernode.java.reflect;

import com.bjpowernode.java.bean.Vip;

import java.lang.reflect.Constructor;

/*
比上一個例子(ReflectTest11)重要一些!!!

通過反射機制呼叫構造方法例項化java物件。(這個不是重點)
 */
public class ReflectTest12 {
    public static void main(String[] args) throws Exception{
        // 不使用反射機制怎麼建立物件
        Vip v1 = new Vip();
        Vip v2 = new Vip(110, "zhangsan", "2001-10-11", true);

        // 使用反射機制怎麼建立物件呢?
        Class c = Class.forName("com.bjpowernode.java.bean.Vip");
        // 呼叫無引數構造方法
        Object obj = c.newInstance();
        System.out.println(obj);

        // 呼叫有引數的構造方法怎麼辦?
        // 第一步:先獲取到這個有引數的構造方法
        Constructor con = c.getDeclaredConstructor(int.class, String.class, String.class,boolean.class);
        // 第二步:呼叫構造方法new物件
        Object newObj = con.newInstance(110, "jackson", "1990-10-11", true);
        System.out.println(newObj);

        // 獲取無引數構造方法
        Constructor con2 = c.getDeclaredConstructor();
        Object newObj2 = con2.newInstance();
        System.out.println(newObj2);
    }
}

ReflectTest13——給你一個類,怎麼獲取這個類的父類,已經實現了哪些介面?

package com.bjpowernode.java.reflect;

/*
重點:給你一個類,怎麼獲取這個類的父類,已經實現了哪些介面?
 */
public class ReflectTest13 {
    public static void main(String[] args) throws Exception{

        // String舉例
        Class stringClass = Class.forName("java.lang.String");

        // 獲取String的父類
        Class superClass = stringClass.getSuperclass();
        System.out.println(superClass.getName());

        // 獲取String類實現的所有介面(一個類可以實現多個介面。)
        Class[] interfaces = stringClass.getInterfaces();
        for(Class in : interfaces){
            System.out.println(in.getName());
        }
    }
}