一、靜態語言與動態語言
靜態類型語言:是指在編譯時變量的數據類型即可確定的語言,多數靜態類型語言要求在使用變量之前必須聲明數據類型,某些具有類型推導能力的現代語言可能能夠部分減輕這個要求。強類型
動態類型語言:是在運行時確定數據類型的語言。變量使用之前不需要類型聲明,通常變量的類型是被賦值的那個值的類型。弱類型
靜態:C#,Java,C,C++
動態:JavaScript,python,perl,ruby,php
靜態類型語言因為類型強制聲明,所以IDE可以做到很好的代碼感知能力,因為有IDE的撐腰,所以開發大型系統,復雜系統比較有保障。
靜態語言相對比較封閉的特點,使得第三方開發包對代碼的侵害性可以降到很低。動態語言在這點上表現的就比較差,我想大家都有過從網上下載某個JS包,然后放到項目代碼里發生沖突的經歷。
二、反射概要
2.1、概念
反射就是可以在運行時加載類信息,並可以創建其對象訪問其屬性和方法。這種動態獲取的信息以及動態調用對象的方法的功能稱為java語言的反射機制。
通俗說:運行時獲得並使用類的信息的方法叫反射
2.2、功能
- 在運行時判斷任意一個對象所屬的類;
- 在運行時構造任意一個類的對象;
- 在運行時判斷任意一個類所具有的成員變量和方法;
- 在運行時調用任意一個對象的方法;生成動態代理。
- 動態獲得類型信息(方法,字段,屬性…)
- 動態時創建對象(根據字符串創建對象)
- 動態調用對象或成員(動態調用方法)
2.3、反射的優點與缺點
優點:
可以實現動態創建對象和編譯,體現出很大的靈活性
缺點:
對性能有影響。使用反射基本上是一種解釋操作,我們可以告訴JVM,我們希望做什么並且它 滿足我們的要求。這類操作總是慢於只直接執行相同的操作。
三、Class
Class 類十分特殊。它和一般類一樣繼承自Object,其實體用以表達Java程序運行時的classes和interfaces,也用來表達enum、array、primitive Java types(boolean, byte, char, short, int, long, float, double)以及關鍵詞void。當一個class被加載,或當加載器(class loader)的defineClass()被JVM調用,JVM 便自動產生一個Class 對象。因為Class並沒有公共的構造方法,只有一個私有的無參構造方法,也就是說 Class 的對象創建只有 JVM 可以完成。
JAVA 使用 Class 對象來執行運行時類型信息(RunTime Type Information,RTTI)。每個類都有一個 Class 對象,它用來創建這個類的所有對象,反過來說,每個類的所有對象都會關聯同一個Class 對象(對於數組來說,維數、類型一致的數組的 Class 對象才是相同的),每個對象的創建都依賴於Class 對象的是否創建,Class 對象的創建發生在類加載(java.lang.ClassLoader)的時候。
理解RTTI在Java中的工作原理,首先需要知道類型信息在運行時是如何表示的,這是由Class對象來完成的,它包含了與類有關的信息。Class對象就是用來創建所有“常規”對象的,Java使用Class對象來執行RTTI,即使你正在執行的是類似類型轉換這樣的操作。
每個類都會產生一個對應的Class對象,也就是保存在.class文件。所有類都是在對其第一次使用時,動態加載到JVM的,當程序創建一個對類的靜態成員的引用時,就會加載這個類。Class對象僅在需要的時候才會加載,static初始化是在類加載時進行的。
獲得Class對象的辦法:
package com.store; public class Fruit { public String name; public static void main(String[] args) throws ClassNotFoundException { Fruit fruit=new Apple(); System.out.println("今天吃:"+fruit.name); // public final native java.lang.Class getClass(); //獲得Class辦法一,通過對象獲得 Class clazz1=fruit.getClass(); //獲得Class辦法二,通過字符串獲得(包名+類名) Class clazz2=Class.forName("com.store.Orange"); //獲得Class辦法三,(通過類的靜態成員class獲得) Class clazz3=Fruit.class; //獲得Class辦法四,(只針對內置的基本數據類型) Class clazz4=Integer.TYPE; //獲得父類類型 Class clazz5=clazz2.getSuperclass(); System.out.println("類名:"+clazz1.getName()); System.out.println("類名:"+clazz2.getName()); System.out.println("類名:"+clazz3.getName()); System.out.println("類名:"+clazz4.getName()); System.out.println("com.store.Orange的父類類名:"+clazz5.getName()); } } class Apple extends Fruit{ public Apple() { this.name="蘋果"; } } class Orange extends Fruit{ public Orange() { this.name="橙子"; } }
輸出結果:
四、使用反射
4.1、獲得方法信息
定義一個類:
package com.store; /** * 計算器 * */ public class Calculator { public int n1; public int n2; /**加*/ public void add(){ System.out.println(n1+"+"+n2+"="+(n1+n2)); } /**減*/ public void sub(){ System.out.println(n1+"-"+n2+"="+(n1-n2)); } /**乘*/ public int mut(int a,int b){ System.out.println(a+"X"+b+"="+(a*b)); return a*b; } }
獲得方法信息:
package com.store; import java.lang.reflect.Method; public class Test { public static void main(String[] args) throws ClassNotFoundException { //獲得類型信息 Class clazz1=Class.forName("com.store.Calculator"); //獲得所有方法 Method[] methods=clazz1.getMethods(); for (Method method : methods) { System.out.println(method.getName()+","+method.getParameterCount()); } } }
輸出結果:
4.2、實例化對象
代碼:
package com.store; public class Client { public static void main(String[] args) throws Exception { //根據字符獲得Class對象 Class calcClass=Class.forName("com.store.Calculator"); //根據Class對象創建類的實例,調用無參構造方法 Calculator calc=(Calculator)calcClass.newInstance(); //調用方法 calc.add(); } }
結果:
注意:必須要有無參構造方法,如果通過有參構造方法創建對象則必須先獲得構造信息
4.3、獲得成員變量並賦值
示例:
package com.store; import java.lang.reflect.Field; public class Client { public static void main(String[] args) throws Exception { //根據字符獲得Class對象 Class calcClass=Class.forName("com.store.Calculator"); //根據Class對象創建類的實例,調用無參構造方法 Calculator calc=(Calculator)calcClass.newInstance(); //獲得成名稱為n1的成員變量 Field n1=calcClass.getField("n1"); //賦值,給calc的n1變量賦值 n1.set(calc, 99); //獲得成名稱為n2的成員變量 Field n2=calcClass.getField("n2"); n2.set(calc,88); //調用方法 calc.add(); } }
結果:
4.4、動態調用方法
示例:
package com.store; import java.lang.reflect.Field; import java.lang.reflect.Method; public class CalTest { public static void main(String[] args) throws Exception { /* Calculator calc=new Calculator(); calc.n1=100; calc.n2=55; calc.add(); */ //獲得類型信息 Class<?> calcClazz=Class.forName("com.store.Calculator"); //獲得簡單名稱,沒有包名 System.out.println(calcClazz.getSimpleName()); //實例化對象 Calculator calc=(Calculator) calcClazz.newInstance(); //設置字段的值 Field n1=calcClazz.getField("n2"); n1.set(calc, 99); //調用方法 Method m1=calcClazz.getMethod("sub"); m1.invoke(calc); Method m2=calcClazz.getMethod("mut",Integer.TYPE,Integer.TYPE); m2.invoke(calc,9,9); } }
結果:
4.5、動態實例化對象示例
示例:
package com.store; import java.util.Scanner; public class ObjectTest { public static void main(String[] args) throws Exception { Scanner input=new Scanner(System.in); System.out.print("請問您要吃點什么?:"); String type=input.nextLine(); Class<?> clazz=Class.forName("com.store."+type); if(clazz==null){ System.out.println("這個真沒有"); }else{ Fruit fruit=(Fruit) clazz.newInstance(); System.out.println("這是您的"+fruit.name+",請慢用。"); } } }
結果:
package com.store; public class Banana extends Fruit{ public Banana() { this.name="香蕉"; } }
這里添加一個Banana類,不需要修改其它代碼但增加的功能實現了,滿足OCP(對擴展開放對修改關閉)原則。
【案例1】通過一個對象獲得完整的包名和類名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
package
Reflect;
/**
* 通過一個對象獲得完整的包名和類名
* */
class
Demo{
//other codes...
}
class
hello{
public
static
void
main(String[] args) {
Demo demo=
new
Demo();
System.out.println(demo.getClass().getName());
}
}
|
【運行結果】:Reflect.Demo
添加一句:所有類的對象其實都是Class的實例。
【案例2】實例化Class類對象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
package
Reflect;
class
Demo{
//other codes...
}
class
hello{
public
static
void
main(String[] args) {
Class<?> demo1=
null
;
Class<?> demo2=
null
;
Class<?> demo3=
null
;
try
{
//一般盡量采用這種形式
demo1=Class.forName(
"Reflect.Demo"
);
}
catch
(Exception e){
e.printStackTrace();
}
demo2=
new
Demo().getClass();
demo3=Demo.
class
;
System.out.println(
"類名稱 "
+demo1.getName());
System.out.println(
"類名稱 "
+demo2.getName());
System.out.println(
"類名稱 "
+demo3.getName());
}
}
|
【運行結果】:
類名稱 Reflect.Demo
類名稱 Reflect.Demo
類名稱 Reflect.Demo
【案例3】通過Class實例化其他類的對象
通過無參構造實例化對象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
package
Reflect;
class
Person{
public
String getName() {
return
name;
}
public
void
setName(String name) {
this
.name = name;
}
public
int
getAge() {
return
age;
}
public
void
setAge(
int
age) {
this
.age = age;
}
@Override
public
String toString(){
return
"["
+
this
.name+
" "
+
this
.age+
"]"
;
}
private
String name;
private
int
age;
}
class
hello{
public
static
void
main(String[] args) {
Class<?> demo=
null
;
try
{
demo=Class.forName(
"Reflect.Person"
);
}
catch
(Exception e) {
e.printStackTrace();
}
Person per=
null
;
try
{
per=(Person)demo.newInstance();
}
catch
(InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
catch
(IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
per.setName(
"Rollen"
);
per.setAge(
20
);
System.out.println(per);
}
}
|
【運行結果】:
[Rollen 20]
但是注意一下,當我們把Person中的默認的無參構造函數取消的時候,比如自己定義只定義一個有參數的構造函數之后,會出現錯誤:
比如我定義了一個構造函數:
1
2
3
4
|
public
Person(String name,
int
age) {
this
.age=age;
this
.name=name;
}
|
然后繼續運行上面的程序,會出現:
java.lang.InstantiationException: Reflect.Person
at java.lang.Class.newInstance0(Class.java:340)
at java.lang.Class.newInstance(Class.java:308)
at Reflect.hello.main(hello.java:39)
Exception in thread "main" java.lang.NullPointerException
at Reflect.hello.main(hello.java:47)
所以大家以后再編寫使用Class實例化其他類的對象的時候,一定要自己定義無參的構造函數
【案例】通過Class調用其他類中的構造函數 (也可以通過這種方式通過Class創建其他類的對象)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
package
Reflect;
import
java.lang.reflect.Constructor;
class
Person{
public
Person() {
}
public
Person(String name){
this
.name=name;
}
public
Person(
int
age){
this
.age=age;
}
public
Person(String name,
int
age) {
this
.age=age;
this
.name=name;
}
public
String getName() {
return
name;
}
public
int
getAge() {
return
age;
}
@Override
public
String toString(){
return
"["
+
this
.name+
" "
+
this
.age+
"]"
;
}
private
String name;
private
int
age;
}
class
hello{
public
static
void
main(String[] args) {
Class<?> demo=
null
;
try
{
demo=Class.forName(
"Reflect.Person"
);
}
catch
(Exception e) {
e.printStackTrace();
}
Person per1=
null
;
Person per2=
null
;
Person per3=
null
;
Person per4=
null
;
//取得全部的構造函數
Constructor<?> cons[]=demo.getConstructors();
try
{
per1=(Person)cons[
0
].newInstance();
per2=(Person)cons[
1
].newInstance(
"Rollen"
);
per3=(Person)cons[
2
].newInstance(
20
);
per4=(Person)cons[
3
].newInstance(
"Rollen"
,
20
);
}
catch
(Exception e){
e.printStackTrace();
}
System.out.println(per1);
System.out.println(per2);
System.out.println(per3);
System.out.println(per4);
}
}
|
【運行結果】:
[null 0]
[Rollen 0]
[null 20]
[Rollen 20]
【案例】
返回一個類實現的接口:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
package
Reflect;
interface
China{
public
static
final
String name=
"Rollen"
;
public
static
int
age=
20
;
public
void
sayChina();
public
void
sayHello(String name,
int
age);
}
class
Person
implements
China{
public
Person() {
}
public
Person(String sex){
this
.sex=sex;
}
public
String getSex() {
return
sex;
}
public
void
setSex(String sex) {
this
.sex = sex;
}
@Override
public
void
sayChina(){
System.out.println(
"hello ,china"
);
}
@Override
public
void
sayHello(String name,
int
age){
System.out.println(name+
" "
+age);
}
private
String sex;
}
class
hello{
public
static
void
main(String[] args) {
Class<?> demo=
null
;
try
{
demo=Class.forName(
"Reflect.Person"
);
}
catch
(Exception e) {
e.printStackTrace();
}
//保存所有的接口
Class<?> intes[]=demo.getInterfaces();
for
(
int
i =
0
; i < intes.length; i++) {
System.out.println(
"實現的接口 "
+intes[i].getName());
}
}
}
|
【運行結果】:
實現的接口 Reflect.China
(注意,以下幾個例子,都會用到這個例子的Person類,所以為節省篇幅,此處不再粘貼Person的代碼部分,只粘貼主類hello的代碼)
【案例】:取得其他類中的父類
1
2
3
4
5
6
7
8
9
10
11
12
13
|
class
hello{
public
static
void
main(String[] args) {
Class<?> demo=
null
;
try
{
demo=Class.forName(
"Reflect.Person"
);
}
catch
(Exception e) {
e.printStackTrace();
}
//取得父類
Class<?> temp=demo.getSuperclass();
System.out.println(
"繼承的父類為: "
+temp.getName());
}
}
|
【運行結果】
繼承的父類為: java.lang.Object
【案例】:獲得其他類中的全部構造函數
這個例子需要在程序開頭添加import java.lang.reflect.*;
然后將主類編寫為:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
class
hello{
public
static
void
main(String[] args) {
Class<?> demo=
null
;
try
{
demo=Class.forName(
"Reflect.Person"
);
}
catch
(Exception e) {
e.printStackTrace();
}
Constructor<?>cons[]=demo.getConstructors();
for
(
int
i =
0
; i < cons.length; i++) {
System.out.println(
"構造方法: "
+cons[i]);
}
}
}
|
【運行結果】:
構造方法: public Reflect.Person()
構造方法: public Reflect.Person(java.lang.String)
但是細心的讀者會發現,上面的構造函數沒有public 或者private這一類的修飾符
下面這個例子我們就來獲取修飾符
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
class
hello{
public
static
void
main(String[] args) {
Class<?> demo=
null
;
try
{
demo=Class.forName(
"Reflect.Person"
);
}
catch
(Exception e) {
e.printStackTrace();
}
Constructor<?>cons[]=demo.getConstructors();
for
(
int
i =
0
; i < cons.length; i++) {
Class<?> p[]=cons[i].getParameterTypes();
System.out.print(
"構造方法: "
);
int
mo=cons[i].getModifiers();
System.out.print(Modifier.toString(mo)+
" "
);
System.out.print(cons[i].getName());
System.out.print(
"("
);
for
(
int
j=
0
;j<p.length;++j){
System.out.print(p[j].getName()+
" arg"
+i);
if
(j<p.length-
1
){
System.out.print(
","
);
}
}
System.out.println(
"){}"
);
}
}
}
|
【運行結果】:
構造方法: public Reflect.Person(){}
構造方法: public Reflect.Person(java.lang.String arg1){}
有時候一個方法可能還有異常,呵呵。下面看看:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
class
hello{
public
static
void
main(String[] args) {
Class<?> demo=
null
;
try
{
demo=Class.forName(
"Reflect.Person"
);
}
catch
(Exception e) {
e.printStackTrace();
}
Method method[]=demo.getMethods();
for
(
int
i=
0
;i<method.length;++i){
Class<?> returnType=method[i].getReturnType();
Class<?> para[]=method[i].getParameterTypes();
int
temp=method[i].getModifiers();
System.out.print(Modifier.toString(temp)+
" "
);
System.out.print(returnType.getName()+
" "
);
System.out.print(method[i].getName()+
" "
);
System.out.print(
"("
);
for
(
int
j=
0
;j<para.length;++j){
System.out.print(para[j].getName()+
" "
+
"arg"
+j);
if
(j<para.length-
1
){
System.out.print(
","
);
}
}
Class<?> exce[]=method[i].getExceptionTypes();
if
(exce.length>
0
){
System.out.print(
") throws "
);
for
(
int
k=
0
;k<exce.length;++k){
System.out.print(exce[k].getName()+
" "
);
if
(k<exce.length-
1
){
System.out.print(
","
);
}
}
}
else
{
System.out.print(
")"
);
}
System.out.println();
}
}
}
|
【運行結果】:
public java.lang.String getSex ()
public void setSex (java.lang.String arg0)
public void sayChina ()
public void sayHello (java.lang.String arg0,int arg1)
public final native void wait (long arg0) throws java.lang.InterruptedException
public final void wait () throws java.lang.InterruptedException
public final void wait (long arg0,int arg1) throws java.lang.InterruptedException
public boolean equals (java.lang.Object arg0)
public java.lang.String toString ()
public native int hashCode ()
public final native java.lang.Class getClass ()
public final native void notify ()
public final native void notifyAll ()
【案例】接下來讓我們取得其他類的全部屬性吧,最后我講這些整理在一起,也就是通過class取得一個類的全部框架
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
class
hello {
public
static
void
main(String[] args) {
Class<?> demo =
null
;
try
{
demo = Class.forName(
"Reflect.Person"
);
}
catch
(Exception e) {
e.printStackTrace();
}
System.out.println(
"===============本類屬性========================"
);
// 取得本類的全部屬性
Field[] field = demo.getDeclaredFields();
for
(
int
i =
0
; i < field.length; i++) {
// 權限修飾符
int
mo = field[i].getModifiers();
String priv = Modifier.toString(mo);
// 屬性類型
Class<?> type = field[i].getType();
System.out.println(priv +
" "
+ type.getName() +
" "
+ field[i].getName() +
";"
);
}
System.out.println(
"===============實現的接口或者父類的屬性========================"
);
// 取得實現的接口或者父類的屬性
Field[] filed1 = demo.getFields();
for
(
int
j =
0
; j < filed1.length; j++) {
// 權限修飾符
int
mo = filed1[j].getModifiers();
String priv = Modifier.toString(mo);
// 屬性類型
Class<?> type = filed1[j].getType();
System.out.println(priv +
" "
+ type.getName() +
" "
+ filed1[j].getName() +
";"
);
}
}
}
|
【運行結果】:
===============本類屬性========================
private java.lang.String sex;
===============實現的接口或者父類的屬性========================
public static final java.lang.String name;
public static final int age;
【案例】其實還可以通過反射調用其他類中的方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
class
hello {
public
static
void
main(String[] args) {
Class<?> demo =
null
;
try
{
demo = Class.forName(
"Reflect.Person"
);
}
catch
(Exception e) {
e.printStackTrace();
}
try
{
//調用Person類中的sayChina方法
Method method=demo.getMethod(
"sayChina"
);
method.invoke(demo.newInstance());
//調用Person的sayHello方法
method=demo.getMethod(
"sayHello"
, String.
class
,
int
.
class
);
method.invoke(demo.newInstance(),
"Rollen"
,
20
);
}
catch
(Exception e) {
e.printStackTrace();
}
}
}
|
【運行結果】:
hello ,china
Rollen 20
【案例】調用其他類的set和get方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
|
class
hello {
public
static
void
main(String[] args) {
Class<?> demo =
null
;
Object obj=
null
;
try
{
demo = Class.forName(
"Reflect.Person"
);
}
catch
(Exception e) {
e.printStackTrace();
}
try
{
obj=demo.newInstance();
}
catch
(Exception e) {
e.printStackTrace();
}
setter(obj,
"Sex"
,
"男"
,String.
class
);
getter(obj,
"Sex"
);
}
/**
* @param obj
* 操作的對象
* @param att
* 操作的屬性
* */
public
static
void
getter(Object obj, String att) {
try
{
Method method = obj.getClass().getMethod(
"get"
+ att);
System.out.println(method.invoke(obj));
}
catch
(Exception e) {
e.printStackTrace();
}
}
/**
* @param obj
* 操作的對象
* @param att
* 操作的屬性
* @param value
* 設置的值
* @param type
* 參數的屬性
* */
public
static
void
setter(Object obj, String att, Object value,
Class<?> type) {
try
{
Method method = obj.getClass().getMethod(
"set"
+ att, type);
method.invoke(obj, value);
}
catch
(Exception e) {
e.printStackTrace();
}
}
}
// end class
|
【運行結果】:
男
【案例】通過反射操作屬性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
class
hello {
public
static
void
main(String[] args)
throws
Exception {
Class<?> demo =
null
;
Object obj =
null
;
demo = Class.forName(
"Reflect.Person"
);
obj = demo.newInstance();
Field field = demo.getDeclaredField(
"sex"
);
field.setAccessible(
true
);
field.set(obj,
"男"
);
System.out.println(field.get(obj));
}
}
// end class
|
【案例】通過反射取得並修改數組的信息:
1
2
3
4
5
6
7
8
9
10
11
12
|
import
java.lang.reflect.*;
class
hello{
public
static
void
main(String[] args) {
int
[] temp={
1
,
2
,
3
,
4
,
5
};
Class<?>demo=temp.getClass().getComponentType();
System.out.println(
"數組類型: "
+demo.getName());
System.out.println(
"數組長度 "
+Array.getLength(temp));
System.out.println(
"數組的第一個元素: "
+Array.get(temp,
0
));
Array.set(temp,
0
,
100
);
System.out.println(
"修改之后數組第一個元素為: "
+Array.get(temp,
0
));
}
}
|
【運行結果】:
數組類型: int
數組長度 5
數組的第一個元素: 1
修改之后數組第一個元素為: 100
【案例】通過反射修改數組大小
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
class
hello{
public
static
void
main(String[] args) {
int
[] temp={
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
9
};
int
[] newTemp=(
int
[])arrayInc(temp,
15
);
print(newTemp);
System.out.println(
"====================="
);
String[] atr={
"a"
,
"b"
,
"c"
};
String[] str1=(String[])arrayInc(atr,
8
);
print(str1);
}
/**
* 修改數組大小
* */
public
static
Object arrayInc(Object obj,
int
len){
Class<?>arr=obj.getClass().getComponentType();
Object newArr=Array.newInstance(arr, len);
int
co=Array.getLength(obj);
System.arraycopy(obj,
0
, newArr,
0
, co);
return
newArr;
}
/**
* 打印
* */
public
static
void
print(Object obj){
Class<?>c=obj.getClass();
if
(!c.isArray()){
return
;
}
System.out.println(
"數組長度為: "
+Array.getLength(obj));
for
(
int
i =
0
; i < Array.getLength(obj); i++) {
System.out.print(Array.get(obj, i)+
" "
);
}
}
}
|
【運行結果】:
數組長度為: 15
1 2 3 4 5 6 7 8 9 0 0 0 0 0 0 =====================
數組長度為: 8
a b c null null null null null
動態代理
【案例】首先來看看如何獲得類加載器:
1
2
3
4
5
6
7
8
9
|
class
test{
}
class
hello{
public
static
void
main(String[] args) {
test t=
new
test();
System.out.println(
"類加載器 "
+t.getClass().getClassLoader().getClass().getName());
}
}
|
【程序輸出】:
類加載器 sun.misc.Launcher$AppClassLoader
其實在java中有三種類類加載器。
1)Bootstrap ClassLoader 此加載器采用c++編寫,一般開發中很少見。
2)Extension ClassLoader 用來進行擴展類的加載,一般對應的是jre\lib\ext目錄中的類
3)AppClassLoader 加載classpath指定的類,是最常用的加載器。同時也是java中默認的加載器。
如果想要完成動態代理,首先需要定義一個InvocationHandler接口的子類,已完成代理的具體操作。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
package
Reflect;
import
java.lang.reflect.*;
//定義項目接口
interface
Subject {
public
String say(String name,
int
age);
}
// 定義真實項目
class
RealSubject
implements
Subject {
@Override
public
String say(String name,
int
age) {
return
name +
" "
+ age;
}
}
class
MyInvocationHandler
implements
InvocationHandler {
private
Object obj =
null
;
public
Object bind(Object obj) {
this
.obj = obj;
return
Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
.getClass().getInterfaces(),
this
);
}
@Override
public
Object invoke(Object proxy, Method method, Object[] args)
throws
Throwable {
Object temp = method.invoke(
this
.obj, args);
return
temp;
}
}
class
hello {
public
static
void
main(String[] args) {
MyInvocationHandler demo =
new
MyInvocationHandler();
Subject sub = (Subject) demo.bind(
new
RealSubject());
String info = sub.say(
"Rollen"
,
20
);
System.out.println(info);
}
}
|
【運行結果】:
Rollen 20
類的生命周期
在一個類編譯完成之后,下一步就需要開始使用類,如果要使用一個類,肯定離不開JVM。在程序執行中JVM通過裝載,鏈接,初始化這3個步驟完成。
類的裝載是通過類加載器完成的,加載器將.class文件的二進制文件裝入JVM的方法區,並且在堆區創建描述這個類的java.lang.Class對象。用來封裝數據。 但是同一個類只會被類裝載器裝載以前
鏈接就是把二進制數據組裝為可以運行的狀態。
鏈接分為校驗,准備,解析這3個階段
校驗一般用來確認此二進制文件是否適合當前的JVM(版本),
准備就是為靜態成員分配內存空間,。並設置默認值
解析指的是轉換常量池中的代碼作為直接引用的過程,直到所有的符號引用都可以被運行程序使用(建立完整的對應關系)
完成之后,類型也就完成了初始化,初始化之后類的對象就可以正常使用了,直到一個對象不再使用之后,將被垃圾回收。釋放空間。
當沒有任何引用指向Class對象時就會被卸載,結束類的生命周期
將反射用於工廠模式
先來看看,如果不用反射的時候,的工廠模式吧:
http://www.cnblogs.com/rollenholt/archive/2011/08/18/2144851.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
/**
* @author Rollen-Holt 設計模式之 工廠模式
*/
interface
fruit{
public
abstract
void
eat();
}
class
Apple
implements
fruit{
public
void
eat(){
System.out.println(
"Apple"
);
}
}
class
Orange
implements
fruit{
public
void
eat(){
System.out.println(
"Orange"
);
}
}
// 構造工廠類
// 也就是說以后如果我們在添加其他的實例的時候只需要修改工廠類就行了
class
Factory{
public
static
fruit getInstance(String fruitName){
fruit f=
null
;
if
(
"Apple"
.equals(fruitName)){
f=
new
Apple();
}
if
(
"Orange"
.equals(fruitName)){
f=
new
Orange();
}
return
f;
}
}
class
hello{
public
static
void
main(String[] a){
fruit f=Factory.getInstance(
"Orange"
);
f.eat();
}
}
|
這樣,當我們在添加一個子類的時候,就需要修改工廠類了。如果我們添加太多的子類的時候,改的就會很多。
現在我們看看利用反射機制:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
package
Reflect;
interface
fruit{
public
abstract
void
eat();
}
class
Apple
implements
fruit{
public
void
eat(){
System.out.println(
"Apple"
);
}
}
class
Orange
implements
fruit{
public
void
eat(){
System.out.println(
"Orange"
);
}
}
class
Factory{
public
static
fruit getInstance(String ClassName){
fruit f=
null
;
try
{
f=(fruit)Class.forName(ClassName).newInstance();
}
catch
(Exception e) {
e.printStackTrace();
}
return
f;
}
}
class
hello{
public
static
void
main(String[] a){
fruit f=Factory.getInstance(
"Reflect.Apple"
);
if
(f!=
null
){
f.eat();
}
}
}
|
現在就算我們添加任意多個子類的時候,工廠類就不需要修改。
上面的愛嗎雖然可以通過反射取得接口的實例,但是需要傳入完整的包和類名。而且用戶也無法知道一個接口有多少個可以使用的子類,所以我們通過屬性文件的形式配置所需要的子類。
下面我們來看看: 結合屬性文件的工廠模式
首先創建一個fruit.properties的資源文件,
內容為:
1
2
|
apple=Reflect.Apple
orange=Reflect.Orange
|
然后編寫主類代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
package
Reflect;
import
java.io.*;
import
java.util.*;
interface
fruit{
public
abstract
void
eat();
}
class
Apple
implements
fruit{
public
void
eat(){
System.out.println(
"Apple"
);
}
}
class
Orange
implements
fruit{
public
void
eat(){
System.out.println(
"Orange"
);
}
}
//操作屬性文件類
class
init{
public
static
Properties getPro()
throws
FileNotFoundException, IOException{
Properties pro=
new
Properties();
File f=
new
File(
"fruit.properties"
);
if
(f.exists()){
pro.load(
new
FileInputStream(f));
}
else
{
pro.setProperty(
"apple"
,
"Reflect.Apple"
);
pro.setProperty(
"orange"
,
"Reflect.Orange"
);
pro.store(
new
FileOutputStream(f),
"FRUIT CLASS"
);
}
return
pro;
}
}
class
Factory{
public
static
fruit getInstance(String ClassName){
fruit f=
null
;
try
{
f=(fruit)Class.forName(ClassName).newInstance();
}
catch
(Exception e) {
e.printStackTrace();
}
return
f;
}
}
class
hello{
public
static
void
main(String[] a)
throws
FileNotFoundException, IOException{
Properties pro=init.getPro();
fruit f=Factory.getInstance(pro.getProperty(
"apple"
));
if
(f!=
null
){
f.eat();
}
}
}
|
【運行結果】:Apple
五、注解
5.1、注解的定義
Annontation是Java5開始引入的新特征,中文名稱叫注解。它提供了一種安全的類似注釋的機制,用來將任何的信息或元數據(metadata)與程序元素(類、方法、成員變量等)進行關聯。為程序的元素(類、方法、成員變量)加上更直觀更明了的說明,這些說明信息是與程序的業務邏輯無關,並且供指定的工具或框架使用。Annontation像一種修飾符一樣,應用於包、類型、構造方法、方法、成員變量、參數及本地變量的聲明語句中。
Java注解是附加在代碼中的一些元信息,用於一些工具在編譯、運行時進行解析和使用,起到說明、配置的功能。注解不會也不能影響代碼的實際邏輯,僅僅起到輔助性的作用。包含在 java.lang.annotation 包中。
5.2、注解的用處
1、生成文檔。這是最常見的,也是java 最早提供的注解。常用的有@param @return 等
2、跟蹤代碼依賴性,實現替代配置文件功能。比如Dagger 2依賴注入,未來java開發,將大量注解配置,具有很大用處;
3、在編譯時進行格式檢查。如@override 放在方法前,如果你這個方法並不是覆蓋了超類方法,則編譯時就能檢查出。
5.3、注解的原理
注解本質是一個繼承了Annotation的特殊接口,其具體實現類是Java運行時生成的動態代理類。而我們通過反射獲取注解時,返回的是Java運行時生成的動態代理對象$Proxy1。通過代理對象調用自定義注解(接口)的方法,會最終調用AnnotationInvocationHandler的invoke方法。該方法會從memberValues這個Map中索引出對應的值。而memberValues的來源是Java常量池。
5.4、內置注解
1.)Override
java.lang.Override是一個標記類型注解,它被用作標注方法。它說明了被標注的方法重載了父類的方法,起到了斷言的作用。如果我們使用了這種注解在一個沒有覆蓋父類方法的方法時,java編譯器將以一個編譯錯誤來警示。
2.)Deprecated
Deprecated也是一種標記類型注解。當一個類型或者類型成員使用@Deprecated修飾的話,編譯器將不鼓勵使用這個被標注的程序元素。所以使用這種修飾具有一定的“延續性”:如果我們在代碼中通過繼承或者覆蓋的方式使用了這個過時的類型或者成員,雖然繼承或者覆蓋后的類型或者成員並不是被聲明為@Deprecated,但編譯器仍然要報警。
3.)SuppressWarnings
SuppressWarning不是一個標記類型注解。它有一個類型為String[]的成員,這個成員的值為被禁止的警告名。對於javac編譯器來講,被-Xlint選項有效的警告名也同樣對@SuppressWarings有效,同時編譯器忽略掉無法識別的警告名。
@SuppressWarnings("unchecked")
5.5、元注解
java.lang.annotation提供了四種元注解,專門注解其他的注解(在自定義注解的時候,需要使用到元注解):
@Documented –注解是否將包含在JavaDoc中
@Retention –什么時候使用該注解
@Target –注解用於什么地方
@Inherited – 是否允許子類繼承該注解
1.)@Retention– 定義該注解的生命周期
● RetentionPolicy.SOURCE : 在編譯階段丟棄。這些注解在編譯結束之后就不再有任何意義,所以它們不會寫入字節碼。@Override, @SuppressWarnings都屬於這類注解。
● RetentionPolicy.CLASS : 在類加載的時候丟棄。在字節碼文件的處理中有用。注解默認使用這種方式
● RetentionPolicy.RUNTIME : 始終不會丟棄,運行期也保留該注解,因此可以使用反射機制讀取該注解的信息。我們自定義的注解通常使用這種方式。
非常關鍵,默認設置反射是獲取不到的,設置成RUNTIME在運行時可以獲取
2.)Target – 表示該注解用於什么地方
默認值為任何元素,表示該注解用於什么地方。可用的ElementType參數包括
● ElementType.CONSTRUCTOR:用於描述構造器
● ElementType.FIELD:成員變量、對象、屬性(包括enum實例)
● ElementType.LOCAL_VARIABLE:用於描述局部變量
● ElementType.METHOD:用於描述方法
● ElementType.PACKAGE:用於描述包
● ElementType.PARAMETER:用於描述參數
● ElementType.TYPE:用於描述類、接口(包括注解類型) 或enum聲明
3.)@Documented–表示是否將注解信息添加在java文檔中
4.)@Inherited – 定義該注釋和子類的關系
@Inherited 元注解是一個標記注解,@Inherited闡述了某個被標注的類型是被繼承的。如果一個使用了@Inherited修飾的annotation類型被用於一個class,則這個annotation將被用於該class的子類。
5.6、自定義注解
自定義注解類編寫的一些規則:
1. Annotation型定義為@interface, 所有的Annotation會自動繼承java.lang.Annotation這一接口,並且不能再去繼承別的類或是接口。
2. 參數成員只能用public或默認(default)這兩個訪問權修飾
3. 參數成員只能用基本類型byte,short,char,int,long,float,double,boolean八種基本數據類型和String、Enum、Class、annotations等數據類型,以及這一些類型的數組.
4. 要獲取類方法和字段的注解信息,必須通過Java的反射技術來獲取 Annotation對象,因為你除此之外沒有別的獲取注解對象的方法
5. 注解也可以沒有定義成員,自定義注解需要使用到元注解
5.7、自定義注解實例(結合反射)
使用注解與反射根據Bean生成sql腳本
自定義別名注解Alias:
package com.store; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(value={ElementType.FIELD,ElementType.TYPE}) @Inherited public @interface Alias { //注解的默認成員,default表示默認值 public String value() default ""; //注解成員,是否參與代碼生成 public boolean isGenerator() default true; }
實體類:
package com.store; /** * 產品Bean,實體,模型 */ @Alias(value="products",isGenerator=true) public class Product { private int id; private String name; @Alias(value="money",isGenerator=false) private double price; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } }
反射獲得注解信息
package com.store; import java.lang.reflect.Field; public class GenSQL { public static void main(String[] args) throws Exception { String sql = "select "; Class<?> clazz = Class.forName("com.store.Product"); Field[] fields = clazz.getDeclaredFields(); String temp = ""; for (Field field : fields) { sql += temp + field.getName(); temp = ","; } sql += " from "; // 獲得類的指定注解 Alias classAlias = clazz.getDeclaredAnnotation(Alias.class); if (classAlias != null) { //獲得注解中value的值 sql += classAlias.value(); } System.out.println(sql); } public String toString() { // TODO Auto-generated method stub return super.toString(); } }
運行結果:
六、視頻
https://www.bilibili.com/video/av9219224/
七、作業
1、定義一個父類動物與3個任意子類,通過控制台輸入將實例化的類型名稱,調用bark(叫)這個方法,如輸入"Dog",輸出"狗在叫,汪汪...",使用反射實現
2、定義一個實體類,使用反射生成該實體類對應的select、update、delete、insert與create SQL語句。
3、使用反射與泛型改進JDBCUtils工具類,實現根據SQL返回一個強類型對象,如Product obj=JDBCUtils.getObject<Product>("select * from product where id=1");
4、根據表反向生成實體類與注釋