黑馬程序員-枚舉與反射

------- android培訓java培訓、期待與您交流! ----------

1,爲什麼要使用枚舉?

       枚舉就是要讓某個類型的變量的取值,只能是若干個固定值中的一個,否則,編譯器就會報錯。枚舉可以讓編譯器在編譯的時候就可以控制源程序中填寫的非法值,普通變量的方式在開發階段無法實現這一目標。

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

     私有構造方法

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

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

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

3,普通類模擬枚舉類的基本實現

 public abstract class Weekday {
 private Weekday() {
 };// 構造私有函數

 public final static Weekday SUN = new Weekday() {

  @Override
  public Weekday nextDay() {// 覆蓋父類的方法
   // TODO Auto-generated method stub
   return MON;
  }

 };// 子類的對象
 public final static Weekday MON = new Weekday() {

  @Override
  public Weekday nextDay() {
   // TODO Auto-generated method stub
   return SUN;
  }

 };

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

   4,枚舉類的基本應用

 

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

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

枚舉類的values,valueOf,name,toString,ordinal等方法。

總結:枚舉是一種特殊的類,其中的每個元素都是該類的一個實例對象,例如可以調用WeekDay.SUN.getClass().getName和WeekDay.class.getName()。
   

 5,構造方法的枚舉

   構造方法必須定義成私有的
   如果有多個構造方法,該如何選擇哪個構造方法?
   枚舉元素MON和MON()的效果一樣,都是調用默認的構造方法

public enum Weekday {// 定義枚舉類的關鍵字enum
  SUN(0), MON(), TUE, WED, THI, FRI, SAT;// 枚舉中的每一元素,就相當於這個類的一個實際對象
  // 只要用到了枚舉類,靜態變量都是會初始化。構造方法會執行。
  private Weekday() {
   // 必須放在元素列表之後,元素列表之後要有分號,必須是私有。
   System.out.println("first");
  }

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

}


 

     6,帶有抽象方法的枚舉。

定義枚舉TrafficLamp
實現普通的next方法
實現抽象的next方法:每個元素分別是由枚舉類的子類來生成的實例對象,這些子類採用類似內部類的方式進行定義。
增加上表示時間的構造方法

public enum TrafficLamp {
  // 子類的對象,每個元素都是由他的子類來寫的,必須完成父類的抽象方法。
  RED(30) {
   @Override
   public TrafficLamp nextLamp() {
    // TODO Auto-generated method stub
    return YELLOW;
   }
  },
  GREEN(30) {
   @Override
   public TrafficLamp nextLamp() {
    // TODO Auto-generated method stub
    return YELLOW;
   }
  },
  YELLOW(10) {
   @Override
   public TrafficLamp nextLamp() {
    // TODO Auto-generated method stub
    return RED;
   }
  };
  public abstract TrafficLamp nextLamp();

  private int time;// 希望每個燈都有時間

  private TrafficLamp(int time) {// 通過子類來調用。
   this.time = time;
  }
 }
 // 枚舉只有一個成員時,就可以作爲一種單例的實現方式
}

 


 7,反射的基石 Class類

   Class 的基本分析

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

     人→Person  java類→Class

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

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

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

   當我們在源程序裏面用到Person這個類的時候,首先要從硬盤上,把這個類的二進制類編譯成class放在硬盤上後,把這些二進制代碼加載到內存裏面來,然後在可以創建一個個對象,每一個字節碼都是Class的實例對象

 得到字節碼對應的實例對象(Class類型)

  p1.getClass();得到所屬字節碼

  Class.forName("java.lang.String");指定一個類的完整名稱,得到這個類的字節碼,作用返回字節碼,反射主要用。

  Data.class;類名字節調用

  九個預定義Class實例對象

  void 加上八個基本數據類型

   

public class ReflectTest {
	public static void main(String[] args) throws ClassNotFoundException {
		String str1 = "abc";
		Class cls1 = str1.getClass();
		Class cls2 = String.class;
		Class cls3 = Class.forName("java.lang.String");
		System.out.println(cls1 == cls2);//true
		System.out.println(cls2 == cls3);//true
		System.out.println(cls1.isPrimitive());//是否是原始類型,顯然false
		System.out.println(int.class.isPrimitive());//顯然true;
	}
}

System.out.println(int.class==Integer.TYPE);//true;常量TYPE包裝類型所包裝的基本類型的字節碼

System.ouot.println(int[].class.isArray());//truue;

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

 8,反射的基本概念

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

    例如 getMethod返回Method類型,Method是一種類型。getField返回Field類型,一個java類有很多東西,每一個東西都用對應的類來表示。學習反射,就是把java類中的每一個成分解析成相應的類。這就是Field,Method,Constructor,Package。。。得到這些對象,然後用這些對象,幹一些事情。

9,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();

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

public class ReflectTest {
	public static void main(String[] args) throws ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException {
	
		//new String(new StringBuffer("abc"));利用反射來實現同樣的效果
	 Constructor constru1=	String.class.getConstructor(StringBuffer.class);//jdk可變參數,若干個Class,表示選擇那個構造方法,
	 String str2=(String)constru1.newInstance(new StringBuffer("abc"));//編寫源程序的時候,只知道是constructor,而不知道是哪個構造方法。
	}
}


10,Field類

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

     看代碼實現

    首先定義一個ReflectPoint類

 

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));
	}
}


11,將任意一個對象中的所有String類型的成員變量所對應的字符串內容中的“b”變成“a”;


代碼如下:

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 String toString(){
    	return str1+":"+str2+":"+str3;
    }
}

主函數代碼如下:

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);//把新值給對象
			}
		}
	}
}


 

12,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對象對應的是一個靜態方法。

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

public class ReflectTest {
	public static void main(String[] args) throws Exception {
		
		String str1="abc";
		//str1.charAt(1);
		Method methodCharAt=String.class.getMethod("charAt", int.class);
		System.out.println(methodCharAt.invoke(str1, 1));//,Method的方法,調用,方法對象的方法
		System.out.println(methodCharAt.invoke(null, 1));//如果第一個參數是null,這個方法是靜態的,靜態方法調用不需要對象。
	}

	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);//把新值給對象
			}
		}
	}
}

 寫一個程序根據用戶提供類的類名,去執行該類中的main方法

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

//寫一個程序這個程序根據用戶提供的類名,去執行該類中的main方法。
public class ReflectText2 {
	public static void main(String[] args) throws SecurityException,
			NoSuchMethodException, ClassNotFoundException,
			IllegalArgumentException, IllegalAccessException,
			InvocationTargetException {
		// 普通 方式 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[] args1) {
		// TODO Auto-generated method stub
		for (String arg : args1) {
			System.out.println(arg);
		}
	}
}


13,數組與Object的關係及其反射類型

            

import java.util.Arrays;

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[] { "aa", "bb", "cc" };
		System.out.println(a1.getClass() == a2.getClass());
		// System.out.println(a1.getClass()==a4.getClass());
		// System.out.println(a1.getClass()==a3.getClass());
		System.out.println(a1.getClass().getName());
		System.out.println(a1.getClass().getSuperclass().getName());
		System.out.println(a4.getClass().getSuperclass().getName());
		Object obj1 = a1;
		Object obj2 = a4;
		// Object[] obj3=a1;//數組裏面是int此處錯誤,
		Object obj4 = a3;// 數組裏面裝的是一維數組,jdk1.4
		Object[] obj5 = a4;
		System.out.println(a1);
		System.out.println(a4);
		System.out.println(Arrays.asList(a1));// 整數不轉打印結果[[I@1e5e2c3] jdk1.5一個參數
		System.out.println(Arrays.asList(a4));// jdk 1.4一個數組
	}
}

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

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

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

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

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

14, HashSet,Hashcode分析

   

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;
    }
}
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());
}
}


打印 結果爲2
如果去掉hashcode(),打印結果有可能是2,有可能是3;

   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相同
		// 如果兩個對象equals相等的話,讓他們的hashCode也相等。
		// 經驗:當一個對象被存儲在HashSet集合中以後,以後就不要再修改這個對象中的某些字段了。

		collections.add(pt1);
		collections.add(pt2);
		collections.add(pt3);
		collections.add(pt1);
		pt1.y = 7;
		collections.remove(pt1);
		System.out.println(collections.size());
	}
}

打印 2
改y的值,hashCode值也變了,再刪除的時候,在原來區域找pt1,pt1改變了,所以找不到,內存泄露

15, java框架的概念及反射技術開發框架的原理

使用別人寫的類,有兩種使用方式一種是,你去調用別人的類,二是別人調用你的類 ,

一種叫做工具 一種叫框架 * 區別:工具類被用戶的類調用,而框架則是調用用戶提供的類。

 解決的核心問題: 因爲在寫程序時無法知道要被調用的類名。

所以,在程序中無法直接new * 某個類的實例對象了,而要用反射方式來做。

  

以上是配置文件信息。具體代碼如下:

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.Properties;

/**
 * 使用別人寫的類,有兩種使用方式一種是,你去調用別人的類,二是別人調用你的類 ,一種叫做工具 一種叫框架
 * 區別:工具類被用戶的類調用,而框架則是調用用戶提供的類。 解決的核心問題: 因爲在寫程序時無法知道要被調用的類名。所以,在程序中無法直接new
 * 某個類的實例對象了,而要用反射方式來做。
 * 
 * @author Administrator
 * 
 */
public class TrafficLamp {
	public static void main(String[] args) throws IOException,
			InstantiationException, IllegalAccessException,
			ClassNotFoundException {
		// 得到輸入流
		InputStream input = new FileInputStream("config.properties");
		// Properties 該類主要用於讀取以項目的配置文件以.properties結尾的文件或者xml文件
		Properties prop = new Properties();
		prop.load(input);// Properties對象已經生成,包括文件中的數據
		input.close();
		String className = prop.getProperty(className);// 得到配置文件中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());
	}

}



類加載器的方法得到流對象,一定要記住用完整的路徑,

但完整的路徑不是硬編碼,而是運算出來的,上面的實例可以見下面的優化。

package com.itcast.zhang;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.Collection;
import java.util.Properties;

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());
	}
}


 

16, java裝箱,拆箱

    

public class Test1 {
  
   public static void main(String[] args) {
	  /* Integer i=13;//裝箱
	   System.out.println(i+12);//拆箱
	   Integer i1=13;//裝成Integer對象
	   Integer i2=13;//裝成Integer對象
	   
	   System.out.println(i1==i2);//打印true;
*/	 
	   String s1=new String("abc");
	   String s2=new String("abc");
	   Integer i1=137;//裝成Integer對象
	   Integer i2=137;//裝成Integer對象
	   //-128- 127 會緩存起來。比較小的使用頻率很高。
	   System.out.println(i1==i2);//打印false;
	   //享元設計模式flywight
	   //如果很多很小的對象,他們有很多相同的東西,那就可以把它們變成一個對象,
	   //還有些不同的東西,把他變成外部的屬性,作爲方法的參數傳入。
   }
}



 

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