一、基本概念
(1)java類 用於描述一類事物的共性,只負責告訴我們這個類有什麼屬性,沒有什麼屬性,至於屬性的值是由實例對象來決定的。
(2)類對象和類的對象:類對象:類加載的產物,記錄了類的信息。這個類是Class.
1、補充:
這就能過解釋爲什麼強轉的時候,編譯器會知道,一個類的父類和子類是什麼,因爲信息被一個類記錄了,根據信息,編譯器就能過限制很多寫法。
接口在強轉中比較特殊,是因爲如果是一個接口的話,編譯器可能不會去找它的繼承關係,都認爲有類繼承於它或者它實現別的類。
例子: class Student{}
interface snior extends Student{}//編譯不通過 因爲是接口 所以接口只能繼承接口 不能繼承普通類
這個波重要:這就是可以從技術上解釋這個規範,剩下的就是從生活中解釋規範
2、語法:
Class cls = 字節碼;
可以解釋編譯的情況,編譯的時候是會掃描字節碼文件,如果不存在會報錯。而負責這個的是系統組件ClassLoader,即當遇到一個未知的類時(系統沒有,之前也沒加載的)會生成字節碼,然後後面的使用,才能拿到類的信息。
所以字節碼文件存的是一個類的信息。 Class會創建一個引用指向它。
(3)獲得類對象的方式:
1) 類名.class 直接獲得類對象 還可以獲得基本類型的類對象 double.class
2) 類的對象.getClass() 獲得對應的類對象
3) Class.forName("類的全名") 通過類名獲得類對象 兩個意思:沒有加載的要加載 加載的我要拿到
(4)常見方法
newInstance(): 通過類對象 創建類的對象 (調用類的無參構造方法)
代替了構造方法中的newInstance 簡化了步驟
源代碼也揭示了反射比較耗時,影響效率。
invoke():來自Method方法 對某對象調用該方法
Class.isPrimitive int.class = Integer.TYPE;這個對象是否是基本類型(原始類型)
int[].Class.isPrimitive 是對數組的類型判斷 不是對數組的中的元素類型判斷 所以這個方法的結果不是原始類型 因爲數組是對象類型
int[].Class.isArray 判定原類型是否是數組
(5)9個預定義的的Class文件 8種基本類型和void類型
只要程序中出現的類型,都有各自的Class實例對象。例如int[],void
(6)反射
1、定義:將java類中的各種成分映射成相應的java類
2、應用:
(1)得到一個類的構造方法
String className = "java.lang.String";
Constructor[] constructors = Class.forName(className).getConstructors();
Constructor constructor = Class.forName(className).getConstructor(StringBuffer.class);
System.out.println(constructors.length);
System.out.println(constructor.toString());
Constructor constructor = Class.forName(className).getConstructor(2);//這句代碼執行不通過 由於構造方法是並列的,不能按照順序取出構造方法。
getConstructor()方法的參數列表形式:Class<?> parameterType
getConstructor(StringBuffer.class,int.class);//編譯不通過,要求可變的參數的類型都是相同的
//構造的方法的newInstance()
String className = "java.lang.String";
Constructor constructor = Class.forName(className)
.getConstructor(StringBuffer.class);//選擇StringBuffer
//構造方法
String s = (String) constructor.newInstance(
new StringBuffer("a"));//用這個對象的時候要
//傳一個這個StringBuffer參數
//不好的地方 程序猿可能不知道constructor的所屬是Data還是String
//的構造方法
(2)得到成員變量
class ReflectPoint{
private int x;
public int y;
public ReflectPoint(int x, int y) {
super();
this.x = x;
this.y = y;
}
}
public static void main(String[] args) throws Exception{
/*ReflectPoint rflctPoint = new ReflectPoint(1,2);
//得到字節碼,拿到成員變量的信息
Class cls = rflctPoint.getClass();
//拿到要求的、特定的成員變量的信息
Field fld = cls.getField("y");
//根據拿到的信息,實例化一個信息
System.out.println(fld.get(rflctPoint));*/
//暴力反射訪問私有
/*ReflectPoint r = new ReflectPoint(4,5);
Class cls = r.getClass();
Field fld = cls.getDeclaredField("x");//能夠看到了 但拿不到
fld.setAccessible(true);//我要拿到它
System.out.println(fld.get(r));*///拿到了
}
//例子
public class practice {
public static void main(String[] args) throws Exception{
ReflectPoint f = new ReflectPoint(1,2);
changeValue(f);
System.out.println(f);
}
public static void changeValue (Object obj)throws Exception{
Field[] flds = obj.getClass().getDeclaredFields();
//System.out.println(flds.length);
for(Field fld:flds){
//System.out.println(fld.getType()==String.class);
if(fld.getType()==String.class){
String oldValue =(String)fld.get(obj);
String newValue = oldValue.replace('b', '2');
System.out.println(newValue);
fld.set(obj, newValue);
}
}
}
}
class ReflectPoint{
private int x;
public int y;
public String s1 = "abc";
public String s2 = "bcd";
public String s3 = "cde";
public ReflectPoint(int x, int y) {
//super();
this.x = x;
this.y = y;
}
@Override
public String toString() {
return "ReflectPoint [s1=" + s1 + ", s2=" + s2 + ", s3=" + s3 + ", x="
+ x + ", y=" + y + "]";
}
}
(3)方法
//得到String中CahrAt的方法
Method mthd = String.class.getMethod("charAt", int.class);
String s = "abc";
//再得到特定對象的CharAt的方法
System.out.println(mthd.invoke(s,1));//s如果是一個null表示調用的是靜態方法
(4)對接收數組參數的成員方法進行反射
public class practice {
public static void main(String[] args) throws Exception{
//根據用戶給我的類,我來調用它裏面的main方法
//找到用戶給我類的Class信息
String className = "test.Test";
Class cls = Class.forName(className);
//根據給的信息,找到需要的方法
Method mtd = cls.getMethod("main", String[].class);
//調用該方法
mtd.invoke(null, (Object)new String[]{"aaa","bbb","ccc"});
//mtd.invoke(null, new Object[]{new String[]{"aaa","bbb","ccc"}});
//mtd.invoke(null, new String[]{"aaa","bbb","ccc"});//這個方法不會出結果,因爲爲了兼容5.0一下版本,會將這個數組打散成三個元素,而main中的參數要求是一個對象類型 的單個元素。前兩種方法是解決辦法。
}
}
class Test{
public static void main(String[] args){
for(String arg:args){
System.out.println(arg);
}
}
}
其中的main方法不是static
public static void main(String[] args) throws Exception{
//對接收數組參數的成員方法進行反射
//根據用戶給我的類,我來調用它裏面的main方法
//找到用戶給我類的Class信息
String className = "test.Test";
Class cls = Class.forName(className);
//根據給的信息,找到需要的方法
Object obj = cls.newInstance();
Method mtd = cls.getMethod("main", String[].class);
//調用該方法
mtd.invoke(obj, (Object)new String[]{"aaa","bbb","ccc"});
//mtd.invoke(null, new Object[]{new String[]{"aaa","bbb","ccc"}});
//mtd.invoke(null, new String[]{"aaa","bbb","ccc"});//這個方法不會出結果,因爲爲了兼容5.0一下版本,會將這個數組打散成三個元素,而main中的參數要求是一個對象類型 的單個元素。前兩種方法是解決辦法。
class Test{
public void main(String[] args){
for(String arg:args){
System.out.println(arg);
}
}
}
(5)數組與Object的關係及反射類型
數組的元素類型和緯度相同,那麼這些數組的Class也相同
例子:int[] a1 = new int[3];
int[] a2 = new int[2];
int[][] a3 = new int[3][4];
String[] a4 = new String[3];
System.out.println(a1.getClass() == a2.getClass());
//System.out.println(a1.getClass() == a3.getClass());
//System.out.println(a1.getClass() == a4.getClass());
System.out.println(a1.getClass().getName());
System.out.println(a3.getClass().getName());
System.out.println(a4.getClass().getName());
System.out.println(a1.getClass().getSuperclass().getName());
System.out.println(a1.getClass().getSuperclass().getName());
Object o1 = a1;
Object o2 = a3;
Object o3 = a4;
//Object[] o3 = a1;//報錯
Object[] o5 = a3;
Object[] o6 = a4;
//這個地方是比較奇特的。。。。用數組對象的引用指向一個數組對象 而不是用一個對象引用指向數組對象
看這個問題的應用:
//這個地方的應用可以解釋這個下面現象,當a1是int時,不會使用4.0的方式
//就是參數不是數組。5.0參數可以變化 String就可以打印出來
System.out.println(Arrays.asList(a1));
System.out.println(Arrays.asList(a4));
System.out.println(Arrays.asList(a3));
(6)數組的反射應用
用反射的方式操作數組,例如:我們常要遍歷數組,或者更改某一個數組元素的值,或者取出某個特定下標的元素。
//數組的反射應用
public void printObject(Object obj){
Class cls = obj.getClass();
if(cls.isArray()){//判斷是否數組
//遍歷數組
//Array利用反射操作數組的工具類
int length = Array.getLength(obj);
for(int i=0;i<length;i++){
System.out.println(Array.get(obj, i));
}
}else{
System.out.println(obj);
}
}
利用反射得到數組的類型的一個問題:
Object[] objs = new Object[]{"1",1};
類型是什麼?
Object[] objs = new Object[]{"1",1};
Object obj = objs[0].getClass().getName();
//String
Object obj = objs[1].getClass().getName();
//Integer
System.out.println(obj);
數組之前所學說,是同類型的元素放到一起,現在看,這個就有問題了。
(7)框架的概念及用反射技術開發框架的原理
我們使用的類有兩種:我們使用別人的:工具類。別人使用我們的:框架。
框架要解決的問題:我們寫的框架,可以調用以後人們寫的類。
1、加載配置文件
2、生成實例newInstance()
package test;
import java.util.*;
import java.io.*;
public class TestReflection {
public void reflectionApp() throws Exception{
InputStream ips = new FileInputStream("config.propertis");
Properties props = new Properties();
props.load(ips);
ips.close();
String className = props.getProperty("className");
Collection<ReflectionPoint> collections = (Collection)Class.forName(className).newInstance();
ReflectionPoint rfctp1 =new ReflectionPoint(3,3);
ReflectionPoint rfctp2 =new ReflectionPoint(3,4);
ReflectionPoint rfctp3 =new ReflectionPoint(3,5);
ReflectionPoint rfctp4 =new ReflectionPoint(3,3);
collections.add(rfctp1);
collections.add(rfctp2);
collections.add(rfctp3);
collections.add(rfctp4);
collections.add(rfctp1);
System.out.println("ArrayList");
for(ReflectionPoint rfctp:collections){
System.out.println(rfctp);
}
collections = new HashSet<ReflectionPoint>();
collections.add(rfctp1);
collections.add(rfctp2);
collections.add(rfctp3);
collections.add(rfctp4);
collections.add(rfctp1);
System.out.println("HashSet");
for(ReflectionPoint rfctp:collections){
System.out.println(rfctp);
}
}
}
class ReflectionPoint{
//屬性
private double x;
private double y;
//構造方法
public ReflectionPoint(){}
public ReflectionPoint(double x,double y){
this.x = x;
this.y = y;
}
//set和get方法
public double getX(){return x;}
public double getY(){return y;}
public void setX(double x){this.x = x;}
public void getY(double y){this.y = y;}
//toString
public String toString(){
return "X = " + x + " Y = " + y;
}
//equals
public boolean equals(Object o){
/*if(this == o) return true;
if(o == null) return false;
if(this.getClass()!=o.getClass())return false;
ReflectionPoint rf = (ReflectionPoint)o;
if(rf.getX()!=x)return false;
if(rf.getY()!=y)return false;
if(rf.getX()!=x)return false;
if(rf.getY()!=y)return false;*/
System.out.println(" equals "+x+"+"+y);
return true;
}
//hashcode
public int hashCode(){
return (int)x;
}
}
(8)用類加載器的方法管理資源和配置文件
配置文件存放路徑:當前工作路徑。
解決配置文件存放路徑遇到的問題:用戶將信息存放在配置文件中,程序是通過get配置文件信息的方式來找到配置文件。簡單來說就是:配置文件管理配置文件。
例子:得到項目的絕對路徑之後找到項目中的配置文件。即:絕對路徑+相對路徑。
應用:將配置文件放到統一的目錄中。
類加載器:可以加載類也可以加載類的配置文件信息。
ClassLoader.getResourceAsStream(String ProjectPath);
ProjectPath = "it\cast\day1\config.properties";
Class.getResourceAsStream(String ProjectParh);
ProjectPath = "config.properties";知道是在我的包裏的。
ProjectPath = "it\cast\day1\config.properties";也可以用這個路徑,我會判斷是否是在我的包裏。 框架中都是將配置文件放到ClassPath中,就是因爲內部的機制是使用類加載器加載的配置文件