天天看點

黑馬程式員——Java基礎---泛型和反射一、泛型二、反射

------ Java教育訓練、Android教育訓練、iOS教育訓練、.Net教育訓練、期待與您交流! -------

泛型和反射

一、泛型

        一、泛型概述

        泛型是JDK1.5版本以後出現的新特性,它主要是在編譯時對類型進行檢查,是一個類型安全機制,用于解決安全問題。

        好處:1、讓運作時異常ClassCastException,轉移到了編譯時期。友善程式員解決問題,讓運作時問題減少,安全。

                   2、避免了強制轉換麻煩。

        泛型的格式為:通過<>來定義要操作的引用資料類型。

        在使用java格式提供的對象時,什麼時候使用泛型呢?通常在集合架構很常見,隻要<>就是用來接收類型的,當使用集合時,将集合中要存儲的資料類型作為參數傳遞到<>中即可。

package com.itheima;
import java.util.*;
public class GenericDemo 
{
	public static void main(String[] args)
	{
		ArrayList<String> al=new ArrayList<String>();
		al.add("張三");
		al.add("李四");
		al.add("王五");
		Iterator<String> it = al.iterator();
		while(it.hasNext())
		{
			System.out.println(it.next());
		}
	}
}
           

        二、泛型類

        在javaapi中有大量的用泛型定義的類,這是因為類中大量的參數和成員變量的引用資料類型是不确定的,這個時候需要在類名之後加上泛型,然後在使用時人為的指定該類中成員變量和參數類型到底是什麼類型的,這樣極大地增強了安全性,不需要再去強制轉換。我們也可以自定義一個泛型類:

package com.itheima;

class Person
{
	
}
class Student
{
	
}
/*
class PersonTool
{
	Object obj;

	public PersonTool(Object obj) {
		super();
		this.obj = obj;
	}
	public Object getobj()
	{
		return obj;
	}
}
*/
class PersonTool<T>
{
	 T t;

	public PersonTool(T t) {
		super();
		this.t = t;
	}
	public T getT()
	{
		return t;
	}
}
public class GenericDemo2 
{
	public static void main(String[] args)
	{
		Student s=new Student();
		Person p1=new Person();
		PersonTool<Student> p=new PersonTool<Student>(s);
		PersonTool<Person> p2=new PersonTool<Person>(p1);
		p.getT();
		p2.getT();
	}
}
           

        三、泛型方法

        當一個類中某一個方法的參數和變量的引用資料類型是不确定的,這是就需要在方法上加上泛型。之是以此時不在類上加泛型是因為泛型類明确泛型類型之後類中方法如果用到泛型則方法的類型也就明确了,可是某一個方法可能要操作的是String類型而另一個類型可能要操作Integer類型,此時明顯使用泛型類是不合适的,這是就用泛型方法。

<span style="font-family: Arial, Helvetica, sans-serif;"></span><pre name="code" class="java">package udp;
class Demo
{
	public<T> void show(T t)
	{
		System.out.println(t);
	}
	public <Q> void print(Q q)//泛型類的泛型定義在傳回值類型之前
	{
		System.out.println(q);
	}
           
public static<W> show1(W w)
           
System.out.println(w);
           
}
}
public class GenericDemo3 
{
	public static void main(String[] args)
	{
		Demo d=new Demo();
		d.show("haha");//明确了泛型為String
		d.print(4);//明确了泛型為Integer
           
Demo.show1(true)//明确泛型為Boolean
	}
}<span style="font-family: Arial, Helvetica, sans-serif;"> </span>
           

        注意:泛型類和泛型方法都是是可以嵌套使用的。

        靜态方法是不可使用定在類上的泛型的,這是因為泛型類是需要對象明确之後泛型才會明确,而靜态方法先于對象存在,随着類的存在而存在,而類存在時該泛型還不明确。如果靜态方法需要用到泛型,則需将泛型定義在方法上。

        四、泛型接口

        對于抽象的接口也可以定義泛型,在實作該接口的時候在接口名之後加上明确的泛型則複寫的時候就不用對參數進行強制轉換,可以直接傳入指定泛型的參數類型。類似于Comparator接口就是這樣。

package udp;
import java.util.*;
class MyCompare implements Comparator<String>
{
	public int compare(String o1, String o2) 
	{
		if (o1.length()>o2.length())
			return -1;
		if (o1.length()<o2.length())
			return 1;
		else
			return o1.compareTo(o2);
	}
}
           

        五、泛型限定

        使用泛型時不能使用類型特有方法!泛型限定主要是使用<?> <? extends T> <? super T>它們分别代表任意類型通配符,該類是T以及T的子類,該類是T以及T的父類。

/*

class Student implements Comparable<Person>//<? super E>
{
	public int compareTo(Person s)
	{
		this.getName()
	}
}
*/
class Comp implements Comparator<Person>
{
	public int compare(Person s1,Person s2)
	{

		//Person s1 = new Student("abc1");
		return s1.getName().compareTo(s2.getName());
	}
}

TreeSet<Student> ts = new TreeSet<Student>(new Comp());
ts.add(new Student("abc1"));
ts.add(new Student("abc2"));
ts.add(new Student("abc3"));
           

二、反射

        一、Class類

        大家都知道,在查閱JavaApi的過程中我們發現Java已經幫我們定義了很多很多的類,這些類描述個各種不同的現實事物,有異常,有集合,有線程等等等,那麼這麼多的類也是不是一個現實存在的事物呢?對的,這些類也是現實存在的事物,我們抽取它們的共性:類名,類的成員變量,類的成員函數,類所處的包等來構成一個描述Java類的類,這個類就叫做Class類。

        那麼根據面向對象的特點,有類就有對象,那麼Class類的對象是什麼呢?從上面的描述可以看出,Class類的對象就是一個一個具體的定義好的類,不管這些類是不是JavaApi的類還是我們自定義的類,說白了,我們往往在運作一個程式的時候首先需要編譯,通過編譯産生的類的位元組碼檔案,這些就是Class類的對象。

        顯然Class類建立對象是不能通過構造函數的,因為Class類是一類非常特殊的類,它是不能通過構造函數來執行個體化對象的,執行個體化Class類有三種方法:

        1、具體某一類的類名.class:Class c1=System.class;

        2、擷取某一個類的對象,然後通過對象的getClass方法擷取Class對象:Student s=new Student(); Class c1=s.getClass();注意該方法是Objcet類的方法。

        3、利用Class類的靜态方法forName(String 類名)來擷取Class對象,該方法若類名是無效的,會抛出異常:Class c1=Class.forName("Student");

        注意,同一個類在記憶體中隻能存在一份位元組碼檔案,意思就是說Class類的對象——某一個類的位元組碼檔案隻能存在一份。方法3字元串一定要是完整的包名+類名。

package udp;
import java.util.*;
public class ReflectDemo 
{
<span style="white-space:pre">	</span>public static void main(String[] args) throws ClassNotFoundException
<span style="white-space:pre">	</span>{
<span style="white-space:pre">		</span>Date s=new Date();
<span style="white-space:pre">		</span>Class c1=Date.class;
<span style="white-space:pre">		</span>Class c2=s.getClass();
<span style="white-space:pre">		</span>Class c3=Class.forName("java.util.Date");//若該字元串不是類名或者該類名不存在則會抛出異常,一定要是完整的包名+類名
<span style="white-space:pre">		</span>System.out.println(c1==c2);
<span style="white-space:pre">		</span>System.out.println(c2==c3);
<span style="white-space:pre">		</span>System.out.println(c1==c3);
<span style="white-space:pre">	</span>}
}
           

        java預定義了九種預定義的Class對象,表示八個基本類型和void。這些對象由Java虛拟機建立,與其表示的基本資料類型同名,即:boolean,byte,short,int,long,char,float,double。

       可以利用Class類中isPrimitive()方法判斷該Class類對象是否是基本資料類型位元組碼。

<pre name="code" class="java">package udp;
import java.util.*;
public class ReflectDemo 
{
	public static void main(String[] args) throws ClassNotFoundException
	{
		Date s=new Date();
		Class c1=Date.class;
		Class c2=s.getClass();
		Class c3=Class.forName("java.util.Date");//若該字元串不是類名或者該類名不存在則會抛出異常
		System.out.println(c1==c2);
		System.out.println(c2==c3);
		System.out.println(c1==c3);
		System.out.println(c1.isPrimitive());//false
		System.out.println(int.class.isPrimitive());//true
		System.out.println(int.class==Integer.class);//false
		System.out.println(int.class==Integer.TYPE);//TYPE是Integer類中靜态變量,它代表Integer包裝的基本資料類型
           
System.out.println(int[].class.isPrimitive());//int數組類型是繼承自Object類,它不是基本資料類型
	}
}
           

       可以利用Class類中isArray()來判斷這個類是否是數組類。System.out.println(int[].class.isArray);

       總之,在源程式中出現的類型,不管是數組類型、void、自定義類還是基本資料類型都有其Class位元組碼檔案——Class對象。

       二、反射

        所謂反射,簡單來說就将一個具體的java類(Class類的對象)的各種成分映射成一個java類。這句話的意思是說,在一個java類中,它必定有各種成分,包括:類名、變量、構造函數、成員方法、包等等等。這些成分也是現實中确實存在的一種事物,根據面向對象的思想,我們可以将這些成分封裝成一個類。

        比如:我們可以将構造函數封裝成一個類,類名叫做:Constructor,這個類專門描述java類中的構造函數,又如:我們将變量封裝成一個類,類名叫做:Field,這個類專門描述java類中的變量這一個事物。

        一個具體的類中的一個成員都可以用相應的javaapi中一個反射類的對象來表示,通過調用Class類的方法可以擷取到這些對象,是以我們可以通過這些對象對這個具體的類的對象的成員進行操作。

        比如:有一個具體的類A,擷取Class對象是A.class,通過Class類的方法擷取了構造函數對象,這個對象代表A的構造函數,然後我們就可以通過這個對象的方法來執行個體化這個A類,因為構造函數本來就是用來執行個體化産生對象的。

        是以,對這些反射類的使用就是我們學習類的重點。

        三、構造函數類Constructor

        構造函數類Constructor是對一個java類A中的構造函數的描述,我們可以通過Class類的方法擷取構造函數對象,然後用構造函數對象的方法來執行個體化這個類A。

import java.lang.reflect.*;
public class ReflectDemo2 
{
	public static void main(String[] args) throws Exception
	{
		//1、擷取String的Class類對象,即String類的位元組碼檔案
		Class c=Class.forName("java.lang.String");
		//2、利用該Class對象擷取String類的一個構造函數對象,若要擷取多個構造函數,則可用getConstructors()方法來獲得一個Constructor[]。
		Constructor con=c.getConstructor(byte[].class);//構造函數有重載形式,該方法類部傳入構造函數參數類型的Class對象來定位哪個構造函數
		//3、利用該構造函數對象初始化String類對象
		String s=(String) con.newInstance(new Object[]{"反射練習".getBytes()});//該函數内部傳入的是構造函數的初始化值,
		System.out.println(s);
	}
}
           

        注意,在使用newInstance這個方法時候,在java1.5之前沒有可變參數,其内部傳入的是Object[]數組,數組内部是構造函數的參數值,當一個arr[]就是構造函數一個值得時候特别要注意在其外面包一層皮new Object[]{arr[]};且該方法傳回的是一個Object對象,是以要強制類型轉換。

        四、變量類Field

        變量類Field是對一個java類A中的成員變量的描述,我們可以通過Class類對象的方法擷取變量類對象,然後用變量類對象的方法來擷取該A 類某對象的這個變量的值或者設定這個值。

package udp;
import java.lang.reflect.*;
public class ReflectDemo3 
{
	public static void main(String[] args) throws Exception
	{
		Person s=new Person("莊三",22);
		//1、擷取該對象的類的位元組碼,即Class的對象
		Class c=s.getClass();
		//2、調用Class對象的方法getField(String 變量名)來擷取變量類的對象,注意這個變量如果是私有的則需要調用getDeclaredField()方法
		Field f2 = c.getField("age");
		Field f1 = c.getDeclaredField("name");//<span style="font-family: Arial, Helvetica, sans-serif;">若要擷取多個構造函數,則可用getFields()方法來獲得一個Field[]。</span>
		//注意,這個變量私有,上面的方法隻是獲得私有變量,但是你還是不能通路這個變量,需要調用變量類對象的setAccessible()方法來擷取通路權限,這就是所謂暴力反射
		f1.setAccessible(true);
		//3、調用變量類對象的get(Object)和set(Object ,value)來擷取某一個對象該變量的值或者設定該值
		System.out.println(f1.get(s));
		System.out.println(f2.get(s));//注意,get()放回的是一個Object
		f1.set(s, "李四");
		f2.set(s, 49);
		System.out.println(s.getName()+","+s.getAge());//判斷這個變量對象是何種類型的變量用getType()方法來獲得該變量類型的位元組碼檔案
	}
}
           

        五、方法類Method

        方法類Method是對一個java類A中的成員方法的描述,我們可以通過Class類對象的方法擷取方法類對象,然後用方法類對象的方法來執行該A類對象的這個方法。

package udp;
import java.lang.reflect.*;
public class ReflectDemo4 
{
	public static void main(String[] args) throws Exception
	{
		Person s=new Person("zhangsan",20);
		//1、擷取該類的位元組碼檔案——Class類對象
		Class c=s.getClass();
		//2、通過該Class類對象的getMethod(方法名,參數類型清單)的方式擷取方法類對象,注意空參數的時候該如何處理
		Method d1=c.getMethod("setName", String.class);
		Method d2=c.getMethod("getName", null);
		//3、利用該方法類對象的invoke(哪個對象的方法,傳入該方法中的實際參數值)來執行該方法,注意空參數的情況
		String ss=(String)d2.invoke(s, null);//若執行該函數有傳回值,invoke傳回該傳回值,以Object的形式!
		System.out.println(ss);
		d1.invoke(s, "李四");
		System.out.println(s.getName());
	}
}
           

        注意,如果是靜态的方法的話則invoke()中第一個參數就是為空的。

        六、注意事項

        注意,反射的這些函數中的參數如果是可變參數形式,在GDK1.5以前就是數組的形式來表示,是以你要傳入一個數組作為參數的時候請一定小心。你要明白你傳入的這個數組是作為可變參數傳進去的還是作為一個數組傳進去的。

        反射的這些方法傳回的值一般都是Object類的,是以一定要強制類型轉換!

------Java教育訓練、Android教育訓練、iOS教育訓練、.Net教育訓練、期待與您交流! -------