Java基礎加強

1、Eclipse的使用 (myeclipse 10)

1工作空間(workspace)、工程(project)

2eclipseJava程序的編寫和運行,及java運行環境的配置。

3快捷鍵的配置,常用快捷鍵:

內容提示:Alt + /         

快速修復:Ctrl + 1

導包:Ctrl + shift + O

格式化代碼塊:ctrl + shift + F

向前向後:Alt + 方向鍵

添加註釋 Ctrl+Shift+/

除去註釋 Ctrl+Shift+\

4程序的調試和運行

F5(跳入)  F6(跳過)   F7(跳出)

Junit

5查看方法說明:F2

重置透視圖

更改爲大寫 Ctrl+Shift+X

更改爲小寫 Ctrl+Shift+Y

複製行 Ctrl+Alt+向下鍵(有些不能用)   -------------- > win7 下更改 快捷鍵  (屏幕分辨率---> 高級設置---> ...)

查看源代碼

1ctrl+T 

2ctrl+shift+T

測試用例

assert 類 api 

2、JDK 5.0 新特性 1.0 

JDK5中新增了很多新的java特性,利用這些新語法可以幫助開發人員編寫出更加高效、清晰,安全的代碼。 

1靜態導入

2自動裝箱/拆箱

3增強for循環

4可變參數

5枚舉

6泛型

7元數據

3、靜態導入

1JDK 1.5 增加的靜態導入語法用於導入類的某個靜態屬性或方法。使用靜態導入可以簡化程序對類靜態屬性和方法的調用。

2語法:

Import static 包名.類名.靜態屬性|靜態方法|*

3例如:

import static java.lang.System.out

import static java.lang.Math.*

4自動裝箱/拆箱

1JDK5.0的語法允許開發人員把一個基本數據類型直接賦給對應的包裝類變量或者賦給 Object 類型的變量,這個過程稱之爲自動裝箱。

2自動拆箱與自動裝箱與之相反,即把包裝類對象直接賦給一個對應的基本類型變量。

3典型應用:

List list = new ArrayList();

list.add(1);

int j = (Integer)list.get(0);

5增強for循環

1引入增強for循環的原因:在JDK5以前的版本中,遍歷數組或集合中的元素,需先獲得數組的長度或集合的迭代器,比較麻煩!

2因此JDK5中定義了一種新的語法——增強for循環,以簡化此類操作。增強for循環只能用在數組、或實現Iterator接口的集合類上

3語法格式:                                              

for(變量類型 變量 :需迭代的數組或集合){

}

例子:

int arr[] = new int[5];

for(int num : arr){

num = 1;

}

System.out.println(arr[0]);

6、可變參數

1測試JDK中具有可變參數的類Arrays.asList()方法。分別傳多個參、傳數組,傳數組又傳參的情況。

注意:傳入基本數據類型數組的問題。

2JDK 5開始, Java 允許爲方法定義長度可變的參數。語法:

public void foo(int … args){

}

3注意事項:

調用可變參數的方法時編譯器將自動創建一個數組保存傳遞給方法的可變參數,因此,程序員可以在方法體中以數組的形式訪問可變參數

可變參數只能處於參數列表的最後所以一個方法最多只能有一個長度可變的參數

7枚舉類

1爲什麼需要枚舉?

一些方法在運行時,它需要的數據不能是任意的,而必須是一定範圍內的值,此類問題在JDK5以前採用自定義帶有枚舉功能的類解決,Java5以後可以直接使用枚舉予以解決。

2JDK 5新增的 enum 關鍵字用於定義一個枚舉類。

3枚舉類具有如下特性:

  1枚舉類也是一種特殊形式的Java類。

  2枚舉類中聲明的每一個枚舉值代表枚舉類的一個實例對象。

  3java中的普通類一樣,在聲明枚舉類時,也可以聲明屬性、方法和構造函數,但枚舉類的構造函數必須爲私有的(這點不難理解)。

  4枚舉類也可以實現接口、或繼承抽象類。

  5JDK5中擴展了swith語句,它除了可以接收int, byte, char, short外,還可以接收一個枚舉類型。

若枚舉類只有一個枚舉值,則可以當作單態設計模式使用。

練習:請編寫一個關於星期幾的枚舉WeekDay,要求:

枚舉值:Mon,Tue,Wed,Thu,Fri,Sat,Sun 

該枚舉要有一個方法,調用該方法返回中文格式的星期。

3Java中聲明的枚舉類,均是java.lang.Enum類的孩子,它繼承了Enum類的所有方法。常用方法:

name()

ordinal()

valueof(Class enumClass, String name)

values() 此方法雖然在JDK文檔中查找不到,但每個枚舉類都具有該方法,它遍歷枚舉類的所有枚舉值非常方便。

8、反射

1什麼是反射?

反射就是把Java類中的各種成分映射成一個個的java對象(加載類,解剖出類的各個組成部分)。例如,一個類有:成員變量,方法,構造方法,包等等信息,利用反射技術可以對一個類進行解剖,把各個組成部分映射成一個個對象。

2反射用在哪裏? 

3學習反射應該掌握些什麼?

9Class

1Class類用於表示.class文件,畫圖演示一個對象的創建過程。

2如何得到某個class文件對應的class對象。

類名.class

對象.getClass()

Class.forName(“類名”

3數組類型的Class實例對象

Class.isArray()

4總之,只要是在源程序中出現的類型,都有各自的Class實例對象,例如,intvoid

10Constructor

1Constructor類的實例對象代表類的一個構造方法。

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

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

3得到某一個構造方法,例:     

Constructor constructor = Class.forName(java.lang.String).getConstructor(StringBuffer.class);

4利用構造方法創建實例對象:

String str = (String)constructor.newInstance(abc);

5Class類的newInstance()方法也可創建類的實例,其內部工作原理是先得無參的構造方法,再用構造方法創建實例對象。

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

(6)String obj =(String)Class.forName(java.lang.String).newInstance();  這個也是 爲什麼 大家在學 基礎班的時候 老師跟你說 如果你的 一個類定義了一個有參的構造函數,那麼還需要在定義一個無參的構造函數.

11Field

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

2問題:得到的Field對象是對應到類上面的成員變量,還是對應到對象上的成員變量?類只有一個,而該類的實例對象有多個,如果是與對象關聯,哪關聯的是哪個對象呢?所以字段fieldX 代表的是x的定義,而不是具體的x變量。(注意訪問權限的問題)

3示例代碼:

ReflectPoint point = new ReflectPoint(1,7);

Field y = Class.forName("cn.itcast.corejava.ReflectPoint").getField("y");

System.out.println(y.get(point));

//Field x = Class.forName("cn.itcast.corejava.ReflectPoint").getField("x");

Field x = Class.forName("cn.itcast.corejava.ReflectPoint").getDeclaredField("x");

x.setAccessible(true);

System.out.println(x.get(point));

12Method

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

2得到類中的某一個方法:

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

3調用方法:

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

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

如果傳遞給Method對象的invoke()方法的第一個參數爲null,這有着什麼樣的意義呢?說明該Method對象對應的是一個靜態方法!

4jdk1.4jdk1.5invoke方法的區別:

Jdk1.5public Object invoke(Object obj,Object... args)

Jdk1.4public Object invoke(Object obj,Object[] args),即按jdk1.4的語法,需要將一個數組作爲參數傳遞給invoke方法時,數組中的每個元素分別對應被調用方法中的一個參數,所以,調用charAt方法的代碼也可以用Jdk1.4改寫爲 charAt.invoke(str, new Object[]{1})形式。

13用反射方式執行某個類中的main方法

1目標:

寫一個程序,這個程序能夠根據用戶提供的類名,去執行該類中的main方法。用普通方式調完後,大家要明白爲什麼要用反射方式去調啊?

2問題:

啓動Java程序的main方法的參數是一個字符串數組,即public static void main(String[] args),通過反射方式來調用這個main方法時,如何爲invoke方法傳遞參數呢?按jdk1.5的語法,整個數組是一個參數,而按jdk1.4的語法,數組中的每個元素對應一個參數,當把一個字符串數組作爲參數傳遞給invoke方法時,javac會到底按照哪種語法進行處理呢?jdk1.5肯定要兼容jdk1.4的語法,會按jdk1.4的語法進行處理,即把數組打散成爲若干個單獨的參數。所以,在給main方法傳遞參數時,不能使用代碼mainMethod.invoke(null,new String[]{xxx})javac只把它當作jdk1.4的語法進行理解,而不把它當作jdk1.5的語法解釋,因此會出現參數類型不對的問題。

3解決辦法:

mainMethod.invoke(null,new Object[]{new String[]{"xxx"}});

mainMethod.invoke(null,(Object)new String[]{"xxx"}); ,編譯器會作特殊處理,編譯時不把參數當作數組看待,也就不會數組打散成若干個參數了

14、內省(Introspector) — JavaBean

1什麼是JavaBean和屬性的讀寫方法?

2訪問JavaBean屬性的兩種方式:

直接調用beansetXXXgetXXX方法。

通過內省技術訪問(java.beans包提供了內省的API),內省技術訪問也提供了兩種方式。

通過PropertyDescriptor類操作Bean的屬性

通過Introspector類獲得Bean對象的 BeanInfo,然後通過 BeanInfo 來獲取屬性的描述器( PropertyDescriptor ),通過這個屬性描述器就可以獲取某個屬性對應的 getter/setter 方法,然後通過反射機制來調用這些方法。

15、內省—beanutils工具包

1Apache組織開發了一套用於操作JavaBeanAPI,這套API考慮到了很多實際開發中的應用場景,因此在實際開發中很多程序員使用這套API操作JavaBean,以簡化程序代碼的編寫。

2Beanutils工具包的常用類:

BeanUtils

PropertyUtils

ConvertUtils.regsiter(Converter convert, Class clazz)

自定義轉換器

16泛型(Generic)—泛形的作用

1JDK5以前,對象保存到集合中就會失去其特性,取出時通常要程序員手工進行類型的強制轉換,這樣不可避免就會引發程序的一些安全性問題。例如:

2ArrayList list = new ArrayList();

list.add("abc");

Integer num = (Integer) list.get(0);  //運行時會出錯,但編碼時發現不了

list.add(new Random());

list.add(new ArrayList());

for(int i=0;i<list.size();i++){

(?)list.get(i);          //此處取出來的對象應轉換成什麼類型

}

3JDK5中的泛形允許程序員在編寫集合代碼時,就限制集合的處理類型,從而把原來程序運行時可能發生問題,轉變爲編譯時的問題,以此提高程序的可讀性和穩定性(尤其在大型程序中更爲突出)

4注意:泛型是提供給javac編譯器使用的,它用於限定集合的輸入類型,讓編譯器在源代碼級別上,即擋住向集合中插入非法數據。但編譯器編譯完帶有泛形的java程序後,生成的class文件中將不再帶有泛形信息,以此使程序運行效率不受到影響,這個過程稱之爲“擦除”。

5泛形的基本術語,以ArrayList<E>爲例:<>念着typeof

ArrayList<E>中的E稱爲類型參數變量

ArrayList<Integer>中的Integer稱爲實際類型參數

整個稱爲ArrayList<E>泛型類型

整個ArrayList<Integer>稱爲參數化的類型ParameterizedType 

17泛型典型應用

1使用迭代器迭代泛形集合中的元素。

2使用增強for循環迭代泛形集合中的元素。

3存取HashMap中的元素。

4使用泛形時的幾個常見問題:

使用泛形時,泛形類型須爲引用類型,不能是基本數據類型

ArrayList<String> list = new ArrayList<Object>();

ArrayList<Object> list = new ArrayList<String>(); 

ArrayList<String> list = new ArrayList ();// 兼容老程序

ArrayList list = new ArrayList<String>();

18自定義泛形——泛型方法

1Java程序中的普通方法、構造方法和靜態方法中都可以使用泛型。方法使用泛形前,必須對泛形進行聲明,語法:<T> T可以是任意字母,但通常必須要大寫。<T>通常需放在方法的返回值聲明之前。例如:

public static <T> void doxx(T t);

2練習:

編寫一個泛形方法,實現指定位置數組元素的交換。

編寫一個泛形方法,接收一個任意數組,並顛倒數組中的所有元素。

3注意:

只有對象類型才能作爲泛型方法的實際參數。

在泛型中可以同時有多個類型,例如:

public static <K,V> V getValue(K key) { return map.get(key);}

/點到任意數組中的指定位置上的兩個元素

//[1,2,3]   0   2    [3,2,1]

public static <T> void m1(T[] t,int index1,int index2){

//緩衝區

T temp = t[index1];

t[index1] = t[index2];

t[index2] = temp;

}

//顛倒任意數組中的元素順序

//[1,2,3]  [3,2,1]

public static <T> void reverse(T[] t){

int startIndex = 0;

int endIndex = t.length -1;

while(startIndex<endIndex){

T temp = t[startIndex];

t[startIndex] = t[endIndex];

t[endIndex] = temp;

        startIndex++;

endIndex--;

}

}

package com.itheima.base;

//把泛型定義在了類上面。但是隻對實例方法有效

public class Demo2<T> {

//<T>泛型的定義,後面的T就是使用了

//必須先定義才能使用。定義的語法就是<T>(字符隨便),但是必須放在返回值的前面

public T findOne(){

return null;

}

public  void get(Class<T> clazz){

}

//靜態方法總是需要單獨定義泛型類型

public static <T> void get1(Class<T> clazz){

}

}

19自定義泛形——泛型類和反射泛形

1如果一個類多處都要用到同一個泛型,這時可以把泛形定義在類上(即類級別的泛型),語法格式如下:

public class GenericDao<T> {

private T field1;

public void save(T obj){}

public T getId(int id){}

}

2注意,靜態方法不能使用類定義的泛形,而應單獨定義泛形。

3泛形的典型應用:BaseDao和反射泛型

package com.itheima.dao;

import java.io.Serializable;

import java.lang.reflect.ParameterizedType;

import org.hibernate.Session;

public class BaseDao<T> implements Dao<T> {

private Session session;

private Class clazz;

// public void setClazz(Class clazz) {//注入

// this.clazz = clazz;

// }

public BaseDao(){//注入

//clazz賦值,知道具體子類的操作對象到底是什麼類型的

Class cz = this.getClass();//當前實例對象

//System.out.println(cz.getName());

//得到參數化的類型

ParameterizedType type = (ParameterizedType)cz.getGenericSuperclass();// BaseDao<Book>   BaseDao<Product>

clazz = (Class)type.getActualTypeArguments()[0];//得到第一個實際的泛型參數類型

}

public void add(T t) {

session.save(t);

}

public void update(T t) {

session.update(t);

}

public void delete(Serializable id) {

T t = findOne(id);

session.delete(t);

}

public T findOne(Serializable id) {

return (T)session.get(clazz, id);

}

}

package com.itheima.dao;

public class Book {

}

package com.itheima.dao;

public interface BookDao extends Dao<Book>{

}

package com.itheima.dao;

public class BookDaoImpl extends BaseDao<Book> implements BookDao {

}

package com.itheima.dao;

public class Client {

public static void main(String[] args) {

//ProductDao pDao = new ProductDaoImpl();

BookDao bDao = new BookDaoImpl();

}

}

package com.itheima.dao;

import java.io.Serializable;

public interface Dao<T> {

/**

 * 執行添加實體的功能

 * @param t

 */

 void add(T t);

 /**

  * 更新記錄

  * @param t

  */

 void update(T t);

/**

 * 按照主鍵刪除記錄

 * @param id

 */

 void delete(Serializable id);

/**

 * 按照主鍵查詢一條記錄

 * @param id

 * @return

 */

T findOne(Serializable id);

}

package com.itheima.dao;

public class Product {

}

package com.itheima.dao;

import java.util.List;

//操作產品的dao

public interface ProductDao extends Dao<Product> {

//根據條件進行查詢

List<Product> findByCondition(String where);

}

package com.itheima.dao;

import java.util.List;

public class ProductDaoImpl extends BaseDao<Product> implements ProductDao {

//public ProductDaoImpl(){

//super(Product.class);

//}

public List<Product> findByCondition(String where) {

return null;

}

}

20、泛型的高級應用——通配符

(1)定義一個方法,接收一個集合,並打印出集合中的所有元素,如下所示:

void print (Collection<String> c) {

           for (String e : c) {

                 System.out.println(e);

           }

2問題:該方法只能打印保存了String對象的集合,不能打印其它集合。通配符用於解決此類問題,方法的定義可改寫爲如下形式:

void print (Collection<?> c)  {//Collection<?>(發音爲:"collection of unknown") 

for (Object e : c) {

System.out.println(e);

}

}

3此種形式下需要注意的是:由於print方法c參數的類型爲Collection<?>,即表示一種不確定的類型,因此在方法體內不能調用與類型相關的方法,例如add()方法。

4總結:使用?通配符主要用於引用對象,使用了?通配符,就只能調對象與類型無關的方法,不能調用對象與類型有關的方法。

import java.util.ArrayList;

import java.util.Collection;

public class Demo3 {

public static void main(String[] args) {

m1(new ArrayList<String>());

}

//泛型通配符

public static void m1(Collection<?> collection){

// collection.add(1);//使用通配符,由於泛型的類型不確定,因此方法內部不能調用與泛型有關的方法

for(Object o:collection)

System.out.println(o);

}

}

21泛型的高級應用——有限制的通配符

1限定通配符的上邊界:

正確:Vector<? extends Number> x = new Vector<Integer>();

錯誤:Vector<? extends Number> x = new Vector<String>();

2限定通配符的下邊界:

正確:Vector<? super Integer> x = new Vector<Number>();

錯誤:Vector<? super Integer> x = new Vector<Byte>();

22Annotation(註解概述

1從 JDK 5.0 開始, Java 增加了對元數據(MetaData) 的支持也就是 Annotation(註解)

2什麼是Annotation,以及註解的作用?三個基本的 Annotation:

@Override: 限定重寫父類方法該註解只能用於方法

@Deprecated: 用於表示某個程序元素(方法等)已過時

@SuppressWarnings: 抑制編譯器警告

package com.itheima.annotation;

import java.util.ArrayList;

import java.util.Date;

import java.util.List;

public class Demo1 {

@SuppressWarnings({"all"})

public static void main(String[] args) {

Date d = new Date();

System.out.println(d.toLocaleString());

List list = new ArrayList();

int i = 10;

mm();

}

@Override

public String toString() {

return super.toString();

}

@Deprecated

public static void mm(){

}

}

3Annotation 其實就是代碼裏的特殊標記它用於替代配置文件,也就是說,傳統方式通過配置文件告訴類如何運行,有了註解技術後,開發人員可以通過註解告訴類如何運行。在Java技術裏註解的典型應用是:可以通過反射技術去得到類裏面的註解,以決定怎麼去運行類。

4掌握註解技術的要點:

如何定義註解

如何反射註解,並根據反射的註解信息,決定如何去運行類

23、自定義 Annotation

1定義新的 Annotation 類型使用 @interface 關鍵字

2聲明註解的屬性

註解屬性的作用:原來寫在配置文件中的信息,可以通過註解的屬性進行描述。

Annotation 的屬性聲明方式:String name();

屬性默認值聲明方式:String name() default xxx;

特殊屬性value:如果註解中有一個名稱value的屬性,那麼使用註解時可以省略value=部分,如@MyAnnotation(xxx")

特殊屬性value[];

package com.itheima.annotation;

//都是Annotation子類

public @interface MyAnnotation1 {

String name();

int age();

String gender() default "male";

MyAnnotation2[] initParam();

}

package com.itheima.annotation;

public @interface MyAnnotation2 {

String paramName() default "";

String paramValue() default "";

}

package com.itheima.annotation;

public @interface MyAnnotation3 {

String value();

String name();

}

package com.itheima.annotation;

public @interface MyAnnotation4 {

 String[] value();

}

package com.itheima.annotation;

public class MyAnnotationTest {

@MyAnnotation1(name = "shit", age = 18, gender = "female", initParam = {

@MyAnnotation2(paramName = "a", paramValue = "b"),

@MyAnnotation2(paramName = "x", paramValue = "y") })

//@MyAnnotation3(value="abc")

@MyAnnotation3(value="abc",name="xxx")//abc就是value屬性賦值

@MyAnnotation4({"abc","def"})

public void m1() {

}

}

24JDK 的元 Annotation

1元 Annotation指修飾AnnotationAnnotationJDK中定義瞭如下元Annotation

2@Retention: 只能用於修飾一個 Annotation 定義用於指定該 Annotation 可以保留的域, @Rentention 包含一個 RetentionPolicy 類型的成員變量通過這個變量指定域。

RetentionPolicy.CLASS: 編譯器將把註解記錄在 class 文件中當運行 Java 程序時, JVM 不會保留註解這是默認值

RetentionPolicy.RUNTIME:編譯器將把註釋記錄在 class 文件中當運行 Java 程序時, JVM 會保留註解程序可以通過反射獲取該註釋

RetentionPolicy.SOURCE: 編譯器直接丟棄這種策略的註釋

3@Target:指定註解用於修飾類的哪個成員. @Target 包含了一個名爲 value,類型爲ElementType的成員變量。

4@Documented: 用於指定被該元 Annotation 修飾的 Annotation 類將被 javadoc 工具提取成文檔.

5@Inherited: 被它修飾的 Annotation 將具有繼承性.如果某個類使用了被 @Inherited 修飾的 Annotation, 則其子類將自動具有該註解

package com.itheima.annotation.app1;

public class UserDaoImplTest {

private UserDaoImpl dao = new UserDaoImpl();

/**

 * 測試添加操作

 */

@MyTest

public void testAddUser() {

dao.addUser();

}

@MyTest

public void testDelUser() {

dao.delUser();

}

}

package com.itheima.annotation.app1;

public class UserDaoImpl {

public void addUser(){

System.out.println("addUser");

}

public void delUser(){

System.out.println("delUser");

}

}

package com.itheima.annotation.app1;

import java.lang.annotation.Documented;

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)

@Target({ElementType.METHOD})

@Documented

public @interface MyTest {

}

public class MyTestRunner {

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

runTest(UserDaoImplTest.class);

}

/**

 * 執行測試

 * @param clazz測試類的的字節碼

 * @throws Exception 

 */

public static void runTest(Class clazz) throws Exception{

//只有@MyTest註解的方法纔是測試方法

//得到測試類中的所有方法   Tips:Class Method Field Constructor都實現了AnnotatedElement接口

Method ms[] = clazz.getMethods();

//遍歷:看看誰的上面有@MyTest

for(Method m:ms){

//誰有就執行誰

boolean b = m.isAnnotationPresent(MyTest.class);

if(b)

m.invoke(clazz.newInstance(), null);

}

}

}

package com.itheima.annotation.app2;

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.METHOD)

public @interface Limit {

float value();

}

package com.itheima.annotation.app2;

public class Client {

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

Account a = new Account(1000000);

a.drawMoney(3000);

}

}

package com.itheima.annotation.app2;

import java.lang.reflect.Method;

public class Account {

private float amount;//餘額

public Account(float amount){//開戶

this.amount = amount;

}

//取款

@Limit(4000)

public void drawMoney(float money) throws Exception{

//先判斷餘額足嗎

if(money>amount)

throw new RuntimeException("餘額不足");

//用傳統方式

//ResourceBundle rb = ResourceBundle.getBundle("com.itheima.annotation.app2.cfg");

//String sLimit = rb.getString("limit");

//用註冊方式

Class clazz = Account.class;

Method m = clazz.getMethod("drawMoney"float.class);//得到取款這個方法

Limit limitAnno = m.getAnnotation(Limit.class);//得到方法上的Limit註解

float limit = limitAnno.value();//取屬性的值

//判斷一次取款是否超限:2K  3K

//float limit = Float.parseFloat(sLimit);

if(money>limit)

throw new RuntimeException("一次取款不能超過"+limit);

amount = amount-money;

System.out.println("您本次取款:"+money+",餘額是:"+amount);

}

}

25、提取 Annotation 信息

1JDK 5.0 在 java.lang.reflect 包下新增了 AnnotationElement 接口該接口代表程序中可以接受註釋的程序元素

2當一個 Annotation 類型被定義爲運行時 Annotation 該註釋纔是運行時可見當 class 文件被載入時保存在 class 文件中的 Annotation 纔會被虛擬機讀取

3程序可以調用 AnnotationElement 對象的如下方法來訪問 Annotation 信息

26、動態代理

1明確兩個概念:

代理對象存在的價值:主要用於攔截對真實業務對象的訪問。

代理對象有什麼方法?

2現在要生成某一個對象的代理對象,這個代理對象通常也要編寫一個類來生成,所以首先要編寫用於生成代理對象的類。

3如何編寫生成代理對象的類,兩個要素:

代理誰

如何生成代理對象

4代理誰?

設計一個類變量,以及一個構造函數,記住代理類 代理哪個對象。

5如何生成代理對象?

設計一個方法生成代理對象(在方法內編寫代碼生成代理對象是此處編程的難點)

6Java提供了一個Proxy類,調用它的newInstance方法可以生成某個對象的代理對象,使用該方法生成代理對象時,需要三個參數:

1.生成代理對象使用哪個類裝載器

2.生成哪個對象的代理對象,通過接口指定

3.生成的代理對象的方法裏幹什麼事,由開發人員編寫handler接口的實現來指定。

7初學者必須理解,或不理解必須記住的2件事情:

Proxy類負責創建代理對象時,如果指定了handler(處理器),那麼不管用戶調用代理對象的什麼方法,該方法都是調用處理器的invoke方法。

由於invoke方法被調用需要三個參數:代理對象、方法、方法的參數,因此不管代理對象哪個方法調用處理器的invoke方法,都必須把自己所在的對象、自己(調用invoke方法的方法)、方法的參數傳遞進來。

package com.itheima.proxy;

public class SpringBrother implements Human {

public void sing(float money) {

System.out.println("拿到"+money+"錢,開唱");

}

public void dance(float money) {

System.out.println("拿到"+money+"錢,開跳");

}

public void eat() {

System.out.println("吃飯");

}

}

package com.itheima.proxy;

public interface Human {

void sing(float money);

void dance(float money);

void eat();

}

package com.itheima.proxy;

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

public class Client {

public static void main(String[] args) {

final Human h = new SpringBrother();

//動態得到代理人

/**

 * loader:代理人使用的類加載器。與被代理人h使用的是一樣的

 * interfaces:代理人要實現的接口。與被代理人h所實現的接口是一樣的

 * InvocationHandler h:策略設計模式。具體該怎麼代理?

 */

Human proxyHuman = (Human)Proxy.newProxyInstance(h.getClass().getClassLoader(), h.getClass().getInterfaces(), new InvocationHandler() {

//具體怎麼代理:具體代理策略

/**

 * 調用被代理人的任何方法都會執行該方法

 * proxy:代理對象的引用

 * method:當前執行的是什麼方法

 * args:當前執行的方法需要的參數

 */

public Object invoke(Object proxy, Method method, Object[] args)

throws Throwable {

// System.out.println("被代理了");

if("sing".equals(method.getName())){

//判斷出場費

float money = (Float)args[0];

if(money>=10000){

return method.invoke(h, money/2);

}

return null;

}else if("dance".equals(method.getName())){

//判斷出場費

float money = (Float)args[0];

if(money>=20000){

return method.invoke(h, money/2);

}

return null;

}else{

return method.invoke(h, args);

}

}

});

proxyHuman.sing(20000);

proxyHuman.dance(30000);

proxyHuman.eat();

}

}

27、動態代理應用

1在動態代理技術裏,由於不管用戶調用代理對象的什麼方法,都是調用開發人員編寫的處理器的invoke方法(這相當於invoke方法攔截到了代理對象的方法調用)。

2並且,開發人員通過invoke方法的參數,還可以在攔截的同時,知道用戶調用的是什麼方法,因此利用這兩個特性,就可以實現一些特殊需求,例如:攔截用戶的訪問請求,以檢查用戶是否有訪問權限、動態爲某個對象添加額外的功能。

28類加載器

1類加載器負責將 .class 文件(可能在磁盤上也可能在網絡上加載到內存中併爲之生成對應的 java.lang.Class 對象

2當 JVM 啓動時,會形成由三個類加載器組成的初始類加載器層次結構: 

29、bootstrap classloader

1bootstrap classloader:引導(也稱爲原始)類加載器,它負責加載Java的核心類。這個加載器的是非常特殊的,它實際上不是 java.lang.ClassLoader的子類,而是由JVM自身實現的。可以通過執行以下代碼來獲得bootstrap classloader加載了那些核心類庫: 

因爲JVM在啓動的時候就自動加載它們,所以不需要在系統屬性CLASSPATH中指定這些類庫

30、extension classloader

extension classloader -擴展類加載器,它負責加載JRE的擴展目錄(JAVA_HOME/jre/lib/ext或者由java.ext.dirs系統屬性指定的)中的JAR包。這爲引入除Java核心類以外的新功能提供了一個標準機制。因爲默認的擴展目錄對所有從同一個JRE中啓動的JVM都是通用的,所以放入這個目錄的 JAR類包對所有的JVMsystem classloader都是可見的。

31、system classloader

1system classloader - 系統(也稱爲應用)類加載器,它負責在JVM被啓動時,加載來自在命令java中的-classpath或者java.class.path系統屬性或者 CLASSPATH操作系統屬性所指定的JAR類包和類路徑。

2可以通過靜態方法ClassLoader.getSystemClassLoader()找到該類加載器。如果沒有特別指定,則用戶自定義的任何類加載器都將該類加載器作爲它的父加載器。

32全盤負責委託機制 :父類委託機制

1classloader 加載類用的是全盤負責委託機制。

2全盤負責:即是當一個classloader加載一個Class的時候,這個Class所依賴的和引用的其它Class通常也由這個classloader負責載入。

3委託機制:先讓parent(父)類加載器 尋找,只有在parent找不到的時候才從自己的類路徑中去尋找。

4類加載還採用了cache機制:如果 cache中保存了這個Class就直接返回它,如果沒有才從文件中讀取和轉換成Class,並存入cache,這就是爲什麼修改了Class但是必須重新啓動JVM才能生效,並且類只加載一次的原因。 

發佈了56 篇原創文章 · 獲贊 5 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章