09_枚舉與反射

一、枚舉

(1)、概念: 枚舉就是要讓某個類型的變量的取值,只能是若干個固定值中的一個,否則,編譯器就會報錯。

(2)、爲什麼要有枚舉:枚舉可以讓編譯器在編譯的時候就可以控制源程序中填寫的非法值,普通變量的方式在開發階段無法實現這一目標。

(3)、用普通類實現枚舉功能,定義一個Weekday的類模擬枚舉的功能。

a、私有構造方法

b、每個元素分別用一個公有的靜態成員變量表示

c、可以有若干公有方法或抽象方法。例如要提供nextDay()是必須抽象的。


每個元素分別用一個公有的靜態成員變量表示可以有若干公有方法或抽象方法。採用抽象方法定義nextDay就將大量的if.else語句轉移成了一個個獨立的類。

(4)、普通類枚舉的基本實現

package cn.hwk;

public abstract class WeekDay {
	// 構造函數私有化
	private WeekDay() {
	}

	public final static WeekDay SUN = new WeekDay() {

		// 覆蓋父類方法
		public WeekDay nextDay() {
			return MON;
		}
	};
	// 子類對象
	public final static WeekDay MON = new WeekDay() {

		public WeekDay nextDay() {
			return SUN;
		}
	};

	// 採用抽象方法定義nextDay()將大量的if else語句轉移成了一個獨立的類  
	public abstract WeekDay nextDay();
	
	/*//不能是靜態,下面是另外一種實現模式
	  public WeekDay nextDay() {
		if (this == SUN) {
			return MON;
		} else {
			return SUN;
		}
	}*/

	public String toString() {
		// 可以寫很長的if eles
		return this == SUN ? "SUN" : "MON";
	}
}

(5)、枚舉的基本應用

package cn.hwk;

public class EnumTest {
	public static void main(String[] args) {
		WeekDay2 weekDay2 = WeekDay2.FRT;
		System.out.println(weekDay2);
		System.out.println(weekDay2.name());// 名字
		System.out.println(weekDay2.ordinal());// 排行,從0開始
		System.out.println(weekDay2.valueOf("SUN").toString());// 把字符串變成對象
		System.out.println(weekDay2.values().length);// 每一個元素都存到數組中。
	}

	// 定義枚舉類的關鍵字enum
	public enum WeekDay2 {
		// 枚舉中的每一元素,就相當於這個類的一 個實際對象
		SUN, MON, TUE, WED, THT, FRT, STA;
	}

}

(6)、構造方法的枚舉

構造方法必須定義成私有的

如果有多個構造方法,該如何選擇哪個構造方法?

枚舉元素MON和MON()的效果一樣,都是調用默認的構造方法

package cn.hwk;

public class EnumTest {
	public static void main(String[] args) {
		
		WeekDay2 weekDay2 = WeekDay2.FRT;
		System.out.println(weekDay2);
		System.out.println(weekDay2.name());// 名字
		System.out.println(weekDay2.ordinal());// 排行,從0開始
		System.out.println(weekDay2.valueOf("SUN").toString());// 把字符串變成對象
		System.out.println(weekDay2.values().length);// 每一個元素都存到數組中。
	}

	// 定義枚舉類的關鍵字enum
	public enum WeekDay2 {
		// 枚舉中的每一元素,就相當於這個類的一 個實際對象
		SUN(1), MON(), TUE, WED, THT, FRT, STA;// 這些元素列表位於必須構造方法之前
		// 構造方法必須是私有的
		private WeekDay2() {
			System.out.println("first");
		}

		private WeekDay2(int day) {
			System.out.println("second");
		}
	}

}

(7)、帶有抽象方法的枚舉

定義枚舉TrafficLamp

實現普通的next方法

實現抽象的next方法:每個元素分別是由枚舉類的子類來生成的實例對象,這些子類採用類似內部類的方式進行定義。

增加上表示時間的構造方法

	//枚舉來寫交通燈控制程序
	public enum TrafficLamp {
		// 子類的對象,每個元素都是由他的子類來寫的,必須完成父類的抽象方法。
		RED(20) {

			public TrafficLamp nextLamp() {
				return GREEN;
			}
		},
		GREEN(30) {

			public TrafficLamp nextLamp() {
				return YELLOW;
			}
		},
		YELLOW(5) {

			public TrafficLamp nextLamp() {
				return RED;
			}
		};
		public abstract TrafficLamp nextLamp();

		// 控制燈亮的時間
		private int time;

		// 通過子類來調用
		private TrafficLamp(int time) {
			this.time = time;
		}
	}
}

枚舉只有一個成員時,可以作爲一種單例的實現方式。

(8)、總結:

枚舉是一種特殊的類,其中的每個元素都是該類的一個實例對象,

例如可以調用WeekDay.SUN.getClass().getName和WeekDay.class.getName()。

二、反射

(1)、反射的基石——Class類

java程序中的各個java類屬於同一類實物,描述這類的java類名就是Class

對比提問:衆多人用什麼表示一個類?衆多Java類用一個什麼表示?

                  人→Person

                  java類→Class

對比提問:Person類代表人,它的實例對象就像張三、李四這樣一個具體的人。Class類代表Java類,它的各個示例對象又分別對應的是什麼呢?

                 對應各個類在內存中的字節碼,如Person類的字節碼,ArrayList類的字節碼,等等

                 一個類被加載器加載到內存中,佔一片儲存空間,這個空間裏的內容就是字節碼,不同類的字節碼是不同的,所以他們在內存中的內容是不同的,這一個個的  

                空間可以分別用一個個對象來表示,這些對象顯然有相同的類型,這個類型又是什麼呢?

如何得到各個字節碼對應的實例對象(Class類型)?

               類名:class,如System.class

               對象:getClass(),如New Data().getClass()

               Class.forName("類名"),Class.forName("java.lang.String")

通過Class可以得到這個類的方方面面的信息。得到類的名字,得到自己所屬的包,得到自己所有的方法列表,得到自己實現的多個接口。

               Person p1=new Person(); Person p2=new Person();

               Class cls1=字節碼1; Class cls2=字節碼2;

九個預定義Class實例對象:void 加上八個基本數據類型(boolean、byte、char、short、int、long、float 和 double)

數組類型的class實例對象:Class.isArray()

總之:只要是在源程序中出現的類型,都有各自的class實例對象,例如,int[  ] ,void...........

package cn.hwk;

public class ReflectTest {
	public static void mian(String[] args) throws Exception {
		String str1 = "abc";

		Class cls1 = str1.getClass();
		Class cls2 = String.class;
		Class cls3 = Class.forName("java.lang.String");
		System.out.println(cls1 == cls2);
		System.out.println(cls1 == cls3);
		
		System.out.println(cls1.isPrimitive());
		System.out.println(int.class.isPrimitive());
		System.out.println(int.class==Integer.class);
		System.out.println(int.class==Integer.TYPE);
		System.out.println(int[].class.isArray());
	}
}

(2)、反射的理解

概念:反射就是把java類中的各種成分映射成相應的java類

理解:例如 getMethod返回Method類型,Method是一種類型。getField返回Field類型,一個java類有很多東西,每一個東西都用對應的類來表示。學習反射,就是把

java類中的每一個成分解析成相應的類。這就是Field,Method,Constructor,Package。。。得到這些對象,然後用這些對象,幹一些事情。

(3)、Constructor類

Constructor類代表某個類的一個構造方法

得到某個類的所有構造方法:

        Constructor[] construcors=Class.forName("java.lang.String").getConstructors();

得到某一個構造方法:

        例如:Constructor constuctor=Class.forName("java.lang.String").getConstructor(StringBuffer.class);//獲得方法時要用到類型

創建實例對象:

        通常方式:String str=new String(new StringBuffer("abc"));

        反射方式:String str=(String)constructor.newInstanct(new StringBuffer("abc"));

        調用獲得的方法時要用到上面相同的實例對象

Class.newInstance()方法:

        該方法內部先得到默認的構造方法,然後用該調用方法創建實例對象

        該方法內部的具體代碼是怎麼寫的呢?用到了緩存機制來保存默認構造方法的實例對象。

        例如:String obj=(String)Class.forName("java.lang.String").newInstance();

package cn.hwk;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class ReflectTest {
	public static void main(String[] args) throws Exception {
		// 利用反射來實現同樣的效果
		// new String(new StringBuffer("abc"));

		// jdk可變參數,若干個Class,表示選擇那個構造方法,
		Constructor constru1 = String.class.getConstructor(StringBuffer.class);

		// 編寫源程序的時候,只知道是constructor,而不知道是哪個構造方法。
		String str2 = (String) constru1.newInstance(new StringBuffer("abc"));
	}
}

(4)、Field類

Field類,代表某個類中的一個成員變量

定義一個ReflectPoint類:

package cn.hwk;

public class ReflectPoint {
	private int x ;
	public int y ;
	
	public ReflectPoint(int x, int y) {
		super();
		this.x = x;
		this.y = y;
	}
}
主函數:
import java.lang.reflect.Constructor;  
import java.lang.reflect.Field;  
import java.lang.reflect.InvocationTargetException;  
  
public class ReflectTest {  
    public static void main(String[] args) throws Exception {  
        ReflectPoint pt1 = new ReflectPoint(3, 5);  
        Field fieldy = pt1.getClass().getField("y");  
        // fieldy不是對象身上的變量,而是類上要用它去取某個對象對應的值。  
        System.out.println(fieldy.get(pt1));  
        // Field  
        // fieldx=pt1.getClass().getField("x");//x私有字段不能這樣使用getFiled("x");,這個只能得到可見的。  
        // System.out.println(fieldx.get(pt1));  
        Field fieldx = pt1.getClass().getDeclaredField("x");  
        fieldx.setAccessible(true);// 暴力反射。不管同意不同意,都要去拿。  
        System.out.println(fieldx.get(pt1));  
    }  
}  

(5)、練習(將任意一個對象中的所有String類型的成員變量所對應的字符串內容中的“b”變成“a”)

代碼:

package cn.hwk;

public class ReflectPoint {
	private int x;
	public int y;

	public ReflectPoint(int x, int y) {
		super();
		this.x = x;
		this.y = y;
	}

	public String st1 = "able";
	public String st2 = "ball";

	public String toString() {
		return st1 + "..." + st2;
	}
}

主函數:

import java.lang.reflect.Constructor;  
import java.lang.reflect.Field;  
import java.lang.reflect.InvocationTargetException;  
  
public class ReflectTest {  
    public static void main(String[] args) throws Exception {  
  
          
        ReflectPoint pt1 = new ReflectPoint(3, 5);  
          
        changeStringValue(pt1);  
        System.out.println(pt1);  
    }  
  
    private static void changeStringValue(Object obj) throws IllegalArgumentException, IllegalAccessException {  
        // TODO Auto-generated method stub  
        //掃描這個類中的所有String類型的變量  
        Field[] fields=obj.getClass().getFields();//得到字節碼,所有可見字段  
        for(Field field:fields){  
            //field.getType().equals(String.class),可以用到等號  
            if(field.getType()==String.class){//對字節碼的比較用等號比較較好,用equals也可,語義不準確  
            String oldValue=(String)field.get(obj);  
            String newValue=oldValue.replace('b', 'a');  
            field.set(obj, newValue);//把新值給對象  
            }  
        }  
    }  
}  

(6)、Method類

Method類代表某個類中的一個成員方法

得到類中的某一個方法

例如:Method charAt=Class.forName("java.lang.String").getMethod("charAt",int.class);

調用方法:

通常方式:System.out.println(str.charAt(1));

反射方式:System.out.println(charAt.invoke(str,1));

如傳遞給Method對象的invoke()方法的第一個參數爲null,這有着什麼樣的意義呢?

說明該Method對象對應的是一個靜態方法。

程序示例(用反射方法執行某個類中的main方法):

package cn.hwk;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

//寫一個程序這個程序根據用戶提供的類名,去執行該類中的main方法。
public class ReflectTest2 {
	public static void main(String[] args) throws Exception {
		// 普通 方式
		// TestArguments.mian(new String[]{"aa","bb"});
		// 反射方式
		String startingClassName = args[0];
		Method mainmethod = Class.forName(startingClassName).getMethod("main",
				String[].class);
		// 編譯器會做特殊處理,編譯時不把參數當做數組看待,也就不會數組打散成若干個元素了
		// mianmethod.invoke(null,new String[]{"aa","bb"});
		// mainmethod.invoke(null, new Object[]{new String[]{"aa","bb"}});
		mainmethod.invoke(null, (Object) new String[] { "aa", "bb" });
	}
}

class TestArguments {
	public static void mian(String[] args) {
		for (String arg : args) {
			System.out.println(arg);
		}
	}
}

(7)、數組與Object的關係及其反射類型

數組的反射:

具有相同維數和元素類型的數組屬於同一個類型,即具有相同的Class實例對象。

代表數組的Class實例對象的getSuperClass()方法返回父類爲Object類對應的Class

基本類型的一維數組可以被當做Object類型使用,不能當做Object[]類型使用:非基本類型的一維數組,既可以當做Object類型使用,又可以當做Object[]類型使用。

Arrays.asList()方法處理int[]和String[]時的差異。(如下面的代碼)

Array工具類用於完成對數組的反射操作。

package cn.hwk;

import java.util.*;

public class ReflectTest3 {

	public static void main(String[] args) {
		int[] a1 = new int[] { 1, 2, 3 };
		int[] a2 = new int[4];
		int[][] a3 = new int[2][3];
		String[] a4 = new String[] { "yaa", "bob", "cpc" };

		System.out.println(a1.getClass() == a2.getClass());// true
		// System.out.println(a1.getClass() == a3.getClass());
		// System.out.println(a1.getClass() == a4.getClass());
		// 以 String 的形式返回此 Class 對象所表示的實體(類、接口、數組類、基本類型或 void)名稱。
		System.out.println(a1.getClass().getName());// [I
		// 返回表示此 Class 所表示的實體(類、接口、基本類型或 void)的超類的 Class。
		System.out.println(a1.getClass().getSuperclass().getName());// java.lang.Object
		System.out.println(a3.getClass().getSuperclass().getName());// java.lang.Object

		Object obj1 = a1;
		Object obj2 = a4;
		// Object[] obj3 = a1;
		Object[] obj4 = a3;
		Object[] obj5 = a4;

		// 打印的是數組對象的hashcode值
		System.out.println(a1);// [I@82764b
		System.out.println(a4);// [Ljava.lang.String;@82764b

		System.out.println(Arrays.asList(a1));// [Ljava.lang.String;@82764b[[I@11671b2]
		System.out.println(Arrays.asList(a4));// [yaa, bob, cpc]
	}

}

(8)、數組的反射應用

package cn.hwk;

import java.lang.reflect.Array;
import java.util.*;

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

		int[] a1 = new int[] { 1, 2, 3 };
		int[] a2 = new int[4];
		int[][] a3 = new int[2][3];
		String[] a4 = new String[] { "yaa", "bob", "cpc" };

		printObject(a1);
		printObject(a4);
		printObject("abv");

	}

	public static void printObject(Object obj) {
		Class clazz = obj.getClass();
		if (clazz.isArray()) {
			int len = Array.getLength(obj);
			for (int i = 0; i < len; i++) {
				System.out.println(Array.get(obj, i));
			}
		} else {
			System.out.println(obj);
		}
	}
}

(9)、ArrayLisr與HashSet的比較、及Hashcode的分析

import java.util.ArrayList;  
import java.util.Collection;  
import java.util.HashSet;  
  
  
public class ReflectTest5 {  
   
  public static void main(String[] args) {  
      Collection collections=new HashSet();//先判斷有沒有,如果有,不加。  
      //Collection collections=new ArrayList();//有序  
       ReflectPoint pt1=new ReflectPoint(3,3);  
       ReflectPoint pt2=new ReflectPoint(5,5);  
       ReflectPoint pt3=new ReflectPoint(3,3);//這裏pt1和pt3相同  
       collections.add(pt1);  
       collections.add(pt2);  
       collections.add(pt3);  
       collections.add(pt1);  
       System.out.println(collections.size());  
     }  
}  <pre name="code" class="java">public class ReflectPoint {  
    private int x;  
    public int y;  
    public String str1 = "ball";  
    public String str2 = "baseketball";  
    public String str3 = "itcast";  
  
    public ReflectPoint(int x, int y) {  
        super();  
        this.x = x;  
        this.y = y;  
    }  
    @Override  
    public int hashCode() {  
        final int prime = 31;  
        int result = 1;  
        result = prime * result + x;  
        result = prime * result + y;  
        return result;  
    }  
    @Override  
    public boolean equals(Object obj) {  
        if (this == obj)  
            return true;  
        if (obj == null)  
            return false;  
        if (getClass() != obj.getClass())  
            return false;  
        ReflectPoint other = (ReflectPoint) obj;  
        if (x != other.x)  
            return false;  
        if (y != other.y)  
            return false;  
        return true;  
    }  
    @Override  
    public String toString(){  
        return str1+":"+str2+":"+str3;  
    }  
}  


(10)、java框架的概念及反射技術開發框架的原理

使用別人寫的類,有兩種使用方式,一種是你去調用別人的類,二是別人調用你的類 ,一種叫做工具 一種叫框架 。

框架與工具類的區別:工具類被用戶的類調用,而框架則是調用用戶提供的類。

解決的核心問題: 因爲在寫程序時無法知道要被調用的類名。所以,在程序中無法直接new  某個類的實例對象了,而要用反射方式來做

配置文件:className=java.util.HashSet

具體實現如下:

package cn.hwk;

import java.io.*;
import java.util.*;

public class ReflectTest5 {
	public static void main(String[] args) throws Exception {
		InputStream ips = new FileInputStream("config.propertis");
		Properties props = new Properties();
		props.load(ips);
		ips.close();
		String className = props.getProperty("className");
		Collection collections = (Collection) Class.forName(className)
				.newInstance();

		ReflectPoint pt1 = new ReflectPoint(3, 3);
		ReflectPoint pt2 = new ReflectPoint(5, 5);
		ReflectPoint pt3 = new ReflectPoint(3, 3);
		collections.add(pt1);
		collections.add(pt2);
		collections.add(pt3);
		collections.add(pt1);

		System.out.println(collections.size());
	}

}

(11)、類加載器的方法管理資源和配置文件

一定要記住用完整的路徑,但完整的路徑不是硬編碼,而是運算出來的

package com.hwk;  
  
import java.io.*;  
import java.util.*;  
  
public class ReflectTest6 {  
    public static void main(String[] args) throws Exception {  
        // 一定要記住用完整的路徑,但完整的路徑不是硬編碼,而是運算出來的  
        // 類加載器 加載配置文件  
        // InputStream input = new FileInputStream("config.properties");  
        // 在classPath指定的目錄下,逐一的去查找你要加載的文件。  
        InputStream input = ReflectTest6.class.getClassLoader()  
                .getResourceAsStream("com/itcast/zhang/config.properties");  
        Properties prop = new Properties();  
        prop.load(input);  
        input.close();  
        String className = prop.getProperty("className");  
        Collection collections = (Collection) Class.forName(className)  
                .newInstance();  
        ReflectPoint pt1 = new ReflectPoint(3, 4);  
        ReflectPoint pt2 = new ReflectPoint(3, 4);  
        System.out.println(collections.size());  
    }  
}  

(12)、內省

內省(Introspector)是Java語言對JavaBean 類屬性、事件的一種缺省處理方法。

   例如類A中有屬性name, 那我們可以通過getName,setName 來得到其值或者設置新的值。通過getName/setName 來訪問name屬性,這就是默認的規則。

   Java中提供了一套API 用來訪問某個屬性的getter/setter方法,通過這些API 可以使你不需要了解這個規則,這些API存放於包java.beans 中。

   一般的做法是通過類Introspector的getBeanInfo方法獲取某個對象的BeanInfo 信息,然後通過BeanInfo來獲取屬性的描述器(PropertyDescriptor),通過這個屬性描述

器就可以獲取某個屬性對應的getter/setter方法,然後我們就可以通過反射機制來調用這些方法。

   我們又通常把JavaBean的實例對象稱之爲值對象(Value Object,簡稱VO),因爲這些bean中通常只有一些信息字段和存儲方法,沒有功能性方法。JavaBean實

際是一種規範,當一個類滿足這個規範,這個類就能被其它特定的類調用。一個類被當作JavaBean使用時,JavaBean的屬性是根據方法名推斷出來的,它根本看不到

Java類內部的成員變量。去掉set前綴,然後取剩餘部分,如果剩餘部分的第二個字母是小寫的,則把剩餘部分的首字母改成小的。就是屬性名。

如:setName()的屬性名——name

        isLast()的屬性名——last

        settime()的屬性名——time

        getCPU()的屬性名——CPU

public class Person {

	private int x;

	public int getAge() {
		return x;
	}

	public void setAge(int age) {
		this.x = age;
	}

}

(13)、JavaBean簡單的內省操作方式

package cn.hwk;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

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

		ReflectPoint pt1 = new ReflectPoint(3, 5);

		String propertyName = "x";

		// "x"-->"X"-->"getX"-->"MethodGetX"
		Object retVal = getProperty(pt1, propertyName);
		System.out.println(retVal);

		Object value = 7;

		setProperties(pt1, propertyName);

		System.out.println(pt1.getX());

	}

	private static void setProperties(Object pt1, String propertyName)
			throws IntrospectionException, IllegalAccessException,
			InvocationTargetException {
		PropertyDescriptor pd2 = new PropertyDescriptor(propertyName,
				pt1.getClass());
		Method methodSetX = pd2.getWriteMethod();
		methodSetX.invoke(pt1, 7);
	}

	private static Object getProperty(Object pt1, String propertyName)
			throws Exception {
		PropertyDescriptor pd = new PropertyDescriptor(propertyName,
				pt1.getClass());
		Method methodGetX = pd.getReadMethod();
		Object retVal = methodGetX.invoke(pt1);
		return retVal;
	}

}<pre name="code" class="java">public class ReflectPoint {
	private int x;
	public int y;
	public String str1 = "ball";
	public String str2 = "baseketball";
	public String str3 = "itcast";

	public ReflectPoint(int x, int y) {
		super();
		this.x = x;
		this.y = y;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + x;
		result = prime * result + y;
		return result;
	}

	public int getX() {
		return x;
	}

	public void setX(int x) {
		this.x = x;
	}

	public int getY() {
		return y;
	}

	public void setY(int y) {
		this.y = y;
	}

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章