本期知識點:
反射
枚舉
JDK新特性
一.反射
1.類的加載及類加載器
a.類的加載:
當程序要使用某個類時,如果該類還未被加載到內存中,則系統會通過加載,連接,初始化三步來實現對這個類進行初始化。
加載:
就是指將class文件讀入內存,併爲之創建一個Class對象。
任何類被使用時系統都會建立一個Class對象。
連接:
驗證 是否有正確的內部結構,並和其他類協調一致
準備 負責爲類的靜態成員分配內存,並設置默認初始化值
解析 將類的二進制數據中的符號引用替換爲直接引用
初始化:
就是以前講過的初始化步驟
b.類的加載時機
創建類的實例
訪問類的靜態變量,或者爲靜態變量賦值
調用類的靜態方法
使用反射方式來強制創建某個類或接口對應的java.lang.Class對象
初始化某個類的子類
直接使用java.exe命令來運行某個主類
c.類加載器
負責將.class文件加載到內在中,併爲之生成對應的Class對象。
雖然我們不需要關心類加載機制,但是瞭解這個機制我們就能更好的理解程序的運行。
d.類加載器的組成
Bootstrap ClassLoader根類加載器
也被稱爲引導類加載器,負責Java核心類的加載
比如System,String等。在JDK中JRE的lib目錄下rt.jar文件中
Extension ClassLoader擴展類加載器
負責JRE的擴展目錄中jar包的加載。
在JDK中JRE的lib目錄下ext目錄
Sysetm ClassLoader系統類加載器
負責在JVM啓動時加載來自java命令的class文件,以及classpath環境變量所指定的jar包和類路徑
class Student {
}
public class ClassDemo {
public static void main(String[] args) throws Exception {
//方式一 Object類中的getClass方法
Student s1 = new Student();
Student s2 = new Student();
Class c1 = s1.getClass();
Class c2 = s2.getClass();
System.out.println(s1==s2);
System.out.println(c1==c2);
//方式二 通過靜態屬性方法
Class c3 = Student.class;
Class c4 = Student.class;
System.out.println(c3==c4);
//方式三 通過Class類裏面的一個靜態方法 forName();
//參數傳該類的全類名 (帶包的類)
Class fn1 = Class.forName("Class.Student");
System.out.println(fn1==c4);
}
}
2.反射
a.概述:
JAVA反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;
對於任意一個對象,都能夠調用它的任意一個方法和屬性;
這種動態獲取的信息以及動態調用對象的方法的功能稱爲java語言的反射機制。
要想解剖一個類,必須先要獲取到該類的字節碼文件對象。
而解剖使用的就是Class類中的方法.所以先要獲取到每一個字節碼文件對應的Class類型的對象.
b.獲取class文件對象的三種方式
i.Object類的getClass()方法
ii.靜態屬性class
iii.Class類中靜態方法forName()
c反射的使用
i.通過反射獲取構造方法並使用
1):獲取所有構造方法
public Constructor<?>[] getConstructors()
獲取所有的構造方法對象數組 不包含私有的
public Constructor<?>[] getDeclaredConstructors()
獲取所有的構造方法對象,包括私有的
2):獲取單個構造方法
public Constructor<T> getConstructor(Class<?>... parameterTypes)
獲取指定的共有的構造方法
public Constructor<T> getDeclaredConstructor(Class<?>...parameterTypes)
獲取指定的共有的或私有的構造方法
ii.通過反射獲取成員變量並使用
1)獲取所有成員變量
public Field[] getFields()
獲取所有的字段 不包含私有字段
public Field[] getDeclaredFields()
獲取所有字段 包含私有字段
2)獲取單個成員變量
public Field getField(String name)
獲取單個的公有字段
public Field getDeclaredField(String name)
獲取單個的公有字段或私有字段
iii.通過反射獲取成員方法並使用
1):獲取所有成員方法
public Method[] getMethods()
獲取所有的共有的方法對象 包括父類的公共方法
public Method[] getDeclaredMethods()
獲取所有的方法對象 包括私有的
2):獲取單個成員方法
public Method getMethod(String name,Class<?>...parameterTypes)
獲取指定的方法對象
public Method getDeclaredMethod(String name,Class<?>...parameterTypes)
獲取私有的方法對象
i.
public class Studnet {
public Studnet() {
System.out.println("這是無參構造");
}
public Studnet(String name) {
System.out.println("這是有參構造"+" "+name);
}
private Studnet(String name , int age) {
System.out.println("有參私有構造"+" "+name+" "+age);
}
}
import java.lang.reflect.Constructor;
public class Demo {
@SuppressWarnings("unchecked")
public static void main(String[] args) throws Exception {
/*
* A:獲取所有構造方法
public Constructor<?>[] getConstructors() 獲取所有的構造方法對象數組 不包含私有的
public Constructor<?>[] getDeclaredConstructors() 獲取所有的構造方法對象,包括私有的
B:獲取單個構造方法
public Constructor<T> getConstructor(Class<?>... parameterTypes)
獲取指定的共有的構造方法
public Constructor<T> getDeclaredConstructor(Class<?>...//獲取指定的共有的或私有的構造方法
parameterTypes)
*/
//獲取所有的構造方法,
Class fn = Class.forName("反射2.Studnet");
Constructor[] a = fn.getConstructors();
for (Constructor con : a) {
System.out.println(con);
}
System.out.println("-----------");
//獲取所有的構造方法,包括私有
Constructor[] dc = fn.getDeclaredConstructors();
for (Constructor con : dc) {
System.out.println(con);
}
System.out.println("-----------");
Constructor c1 = fn.getConstructor();
Object obj1 = c1.newInstance();
System.out.println(obj1);
System.out.println("-----------");
Constructor c2 = fn.getConstructor(String.class);
Object obj2 = c2.newInstance("Yang");
System.out.println(obj2);
System.out.println("-----------");
Constructor c3 = fn.getDeclaredConstructor(String.class, int.class);
c3.setAccessible(true); //取消語法檢查
Object obj3 = c3.newInstance("Ash",21);
System.out.println(obj3);
}
}
ii.
public class Fu {
@Override
public String toString() {
return "Fu [age=" + age + ", sex=" + sex + "]";
}
public int age;
private char sex;
}
public class Zi extends Fu{
public String name;
private int money;
@Override
public String toString() {
return "Zi [name=" + name + ", money=" + money + "]";
}
}
import java.lang.reflect.Field;
public class Demo {
@SuppressWarnings("unchecked")
public static void main(String[] args) throws Exception {
// 1)獲取所有成員變量
// public Field[] getFields() 獲取所有的字段 不包含私有字段
// public Field[] getDeclaredFields() 獲取所有字段 包含私有字段
// 2)獲取單個成員變量
// public Field getField(String name)//獲取單個的公有字段
// public Field getDeclaredField(String name)//獲取單個的公有字段或私有字段
Class fn = Class.forName("反射3.Zi");
Class fn2 = Class.forName("反射3.Fu");
Field[] a = fn.getFields();
for (Field f1 : a) {
System.out.println(f1.getName());
}
System.out.println("----------");
Field[] b = fn.getDeclaredFields();
for (Field f2 : b) {
System.out.println(f2.getName());
}
System.out.println("----------");
Field f3 = fn.getField("name");
Object obj = fn.getConstructor().newInstance();
f3.set(obj, "Ash");
System.out.println(obj);
System.out.println("----------");
Field f4 = fn2.getField("age");
Object obj2 = fn2.getConstructor().newInstance();
f4.set(obj2, 23);
System.out.println(obj2);
System.out.println("----------");
System.out.println("----------");
System.out.println("----------");
}
}
iii.
public class Student {
public void fun(){
System.out.println("this is fun()");
}
public void fun(String name){
System.out.println(name);
}
public int gun (int a){
return a*a;
}
private void hun(int a){
System.out.println(a);
}
}
import java.lang.reflect.Method;
public class Demo {
@SuppressWarnings("unchecked")
public static void main(String[] args) throws Exception {
// 1):獲取所有成員方法
// public Method[] getMethods() //獲取所有的共有的方法對象 包括父類的公共方法
// public Method[] getDeclaredMethods()//獲取所有的方法對象 包括私有的
// 2):獲取單個成員方法
// public Method getMethod(String name,Class<?>...
// parameterTypes)//獲取指定的方法對象
// public Method getDeclaredMethod(String name,Class<?>...//獲取私有的方法對象
// parameterTypes)
Class fn = Class.forName("反射4_成員方法.Student");
Method[] a = fn.getMethods();
for (Method m : a) {
System.out.println(m);
}
System.out.println("-----------");
Method[] b = fn.getDeclaredMethods();
for (Method mm : b) {
System.out.println(mm);
}
System.out.println("-----------");
Method m1 = fn.getMethod("fun",String.class );
Object obj = fn.newInstance();
m1.invoke(obj, "Ying");
System.out.println("-----------");
Method m2 = fn.getDeclaredMethod("hun", int.class);
m2.setAccessible(true);
m2.invoke(obj, 100);
System.out.println("-----------");
Method m3 = fn.getMethod("gun", int.class);
System.out.println(m3.invoke(obj, 2));
System.out.println("-----------");
}
}
3.反射案例
a.通過反射運行配置文件的內容
public class Student {
public void fun(){
System.out.println("this is fun()");
}
}
public class Teaher {
public void fun(){
System.out.println("this is fun()");
}
public void hun(){
System.out.println("this is hun()");
}
}
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Properties;
public class Demo {
@SuppressWarnings("unchecked")
public static void main(String[] args) throws Exception, IOException {
// class_name=反射5_test.Student
// method_name=fun
//讀取配置文件
Properties p = new Properties();
p.load(new FileInputStream("config.txt"));
//獲取某個類字節碼文件對象
Class fn = Class.forName(p.getProperty("class_name"));
//獲取方法
Method dm = fn.getDeclaredMethod(p.getProperty("method_name"));
//執行方法
dm.invoke(fn.newInstance());
}
}
b.通過反射越過泛型檢查
public class Student {
private String name;
private int age;
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
}
import java.lang.reflect.Method;
import java.util.ArrayList;
public class Test {
public static void main(String[] args) throws Exception {
ArrayList<String> a = new ArrayList<>();
a.add("Ying");
//利用反射
Class c = a.getClass();
//獲取add方法對象
Method dm = c.getDeclaredMethod("add", Object.class);
//執行方法
dm.invoke(a, 22);
System.out.println(a);
}
}
c.通過反射給任意的一個對象的任意的屬性賦值爲指定的值
public class Student {
private String name;
private int age;
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
}
import java.lang.reflect.Field;
public class MyTooL {
public MyTooL() {
super();
}
// 提供設置的一個靜態方法
//public void setProperty(Object obj, String propertyName, Object value){}
// obj 給哪個對象設置
// propertyName 字段名稱
// value 字段值
public static void setProperty(Object obj, String propertyName, Object value) throws Exception{
Class class1 = obj.getClass();
Field df = class1.getDeclaredField(propertyName);
df.setAccessible(true);//取消語法檢查
df.set(obj, value);
}
}
//(通過反射寫一個通用的設置某個對象的某個屬性爲指定的值)(理解)
//寫一個工具類 可以給某個對象的某個屬性設置值
public class Demo {
public static void main(String[] args) throws Exception {
Student s = new Student();
MyTooL.setProperty(s, "name", "Ash");
MyTooL.setProperty(s, "age", 20);
System.out.println(s);
}
}
4.動態代理
a.概述:
代理:本來應該自己做的事情,卻請了別人來做,被請的人就是代理對象。
動態代理:
在程序運行過程中產生的這個對象
而程序運行過程中產生對象其實就是我們剛纔反射講解的內容,所以,動態代理其實就是通過反射來生成一個代理
b.Proxy類中的方法創建動態代理類對象
i.概述:
java.lang.reflect
類 Proxy
Proxy 提供用於創建動態代理類和實例的靜態方法,它還是由這些方法創建的所有動態代理類的超類。
ii.方法:
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
(參數:加載器+接口+InvocationHandler子類對象)
返回一個指定接口的代理類實例,該接口可以將方法調用指派到指定的調用處理程序。
iii.Proxy類中創建動態代理對象的方法的三個參數:
ClassLoader對象:定義了由哪個ClassLoader對象來對生成的代理對象進行加載
Interface對象的數組:表示的是我將要給我需要代理的對象提供一組什麼接口,如果我提供了一組接口給它,那麼這個代理對象就宣稱實現了該接口(多態),這樣我就能調用這組接口中的方法了
InvocationHandler對象:表示的是當我這個動態代理對象在調用方法的時候,會關聯到哪一個InvocationHandler對象上
iv.使用:
創建的代理對象是在jvm運行時動態生成的一個對象,它並不是我們的InvocationHandler類型,也不是我們定義的那組接口的類型,而是在運行是動態生成的一個對象,並且命名方式都是這樣的形式,以$開頭,proxy爲中,最後一個數字表示對象的標號。
c.InvocationHandler的方法
i.概述:
java.lang.reflect
接口 InvocationHandler
InvocationHandler 是代理實例的調用處理程序實現的接口。
ii.方法:
Object invoke(Object proxy, Method method, Object[] args) throws Throwable
在代理實例上處理方法調用並返回結果。
在與方法關聯的代理實例上調用方法時,將在調用處理程序上調用此方法。
iii.InvocationHandler接口中invoke方法的三個參數:
proxy:代表動態代理對象
method:代表正在執行的方法
args:代表調用目標方法時傳入的實參
iv.使用:
每一個動態代理類都必須要實現InvocationHandler這個接口,並且每個代理類的實例都關聯到了一個handler,當我們通過代理對象調用一個方法的時候,這個方法的調用就會被轉發爲由InvocationHandler這個接口的invoke 方法來進行調用。
public interface UserOperation {
public abstract void add();
public abstract void delete();
public abstract void update();
public abstract void find();
}
public class UserTool implements UserOperation {
@Override
public void add() {
System.out.println("添加功能");
}
@Override
public void delete() {
System.out.println("刪除功能");
}
@Override
public void update() {
System.out.println("更改功能");
}
@Override
public void find() {
System.out.println("查找功能");
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyInvocationHandler implements InvocationHandler {
private Object obj;
public MyInvocationHandler(Object obj){
this.obj=obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("權限校驗");
Object i = method.invoke(obj, args);
System.out.println("日誌記錄表");
return i;//返回來代理對象
}
}
import java.lang.reflect.Proxy;
public class Demo {
public static void main(String[] args) {
//不用代理直接調用方法
UserOperation u= new UserTool();
u.add();
u.delete();
u.update();
u.find();
System.out.println("----------");
//創建代理對象
//準備對u對象 做一個代理對象
//public static Object newProxyInstance(ClassLoader loader,
// Class<?>[] interfaces, InvocationHandler h)
MyInvocationHandler mi = new MyInvocationHandler(u);
UserOperation newProxy = (UserOperation) Proxy.newProxyInstance(u.getClass().getClassLoader(),
u.getClass().getInterfaces(), mi);
newProxy.add();
newProxy.delete();
newProxy.update();
newProxy.find();
}
}
二.枚舉
1.概述:
是指將變量的值一一列出來,變量的值只限於列舉出來的值的範圍內。
舉例:一週只有7天,一年只有12個月等。
那麼多例類就是一個類有多個實例,但不是無限個數的實例,而是有限個數的實例。這才能是枚舉類
2.枚舉類的引出,通過自己定義一個枚舉類來演示案例?
測試類
public class DirectionDemo {
public static void main(String[] args) {
System.out.println("版本1.0");
Direction d = Direction.QIAN;
System.out.println(d);//地址值 ,說明創建對象了
System.out.println("-------------");
System.out.println("版本2.0");
Direction2 d2 = Direction2.YOU;
System.out.println(d2);
System.out.println(d2.getName());
System.out.println("-------------");
System.out.println("版本3.0");
Direction3 d3 = Direction3.HOU;
System.out.println(d3);
System.out.println(d3.getName());
d3.fun();//編譯看左邊,運行看左邊
System.out.println("版本1.0");
d3 = Direction3.ZUO;
System.out.println(d3);
System.out.println(d3.getName());
d3.fun();
}
}
版本1.0
public class Direction {
//創建實例
public static final Direction QIAN = new Direction();
public static final Direction HOU= new Direction();
public static final Direction ZUO = new Direction();
public static final Direction YOU = new Direction();
//私有構造,別人不能創建
private Direction(){
}
}
版本2.0
public class Direction2 {
//創建實例
public static final Direction2 QIAN = new Direction2("前");
public static final Direction2 HOU= new Direction2("後");
public static final Direction2 ZUO = new Direction2("左");
public static final Direction2 YOU = new Direction2("右");
//加入成員變量
private String name;
private Direction2(String name){
this.name=name;
}
public String getName(){
return name;
}
}
版本3.0
public abstract class Direction3 {
//創建子類實現抽象類的抽象方法
public static final Direction3 QIAN = new Direction3("前"){;
@Override
public void fun() {
System.out.println("前");
}
};
public static final Direction3 HOU= new Direction3("後"){
@Override
public void fun() {
System.out.println("後");
}
};
public static final Direction3 ZUO = new Direction3("左"){
@Override
public void fun() {
// TODO Auto-generated method stub
System.out.println("左");
}
};
public static final Direction3 YOU = new Direction3("右"){
@Override
public void fun() {
// TODO Auto-generated method stub
System.out.println("右");
}
};
//加入成員變量
private String name;
private Direction3(String name){
this.name=name;
}
public String getName(){
return name;
}
//加入抽象方法
public abstract void fun();
}
3.枚舉類
a.格式
格式是:只有枚舉項的枚舉類
public enum 枚舉類名 {
枚舉項1,枚舉項2,枚舉項3…;
}
b.案例演示:
測試類
public class Demo {
public static void main(String[] args) {
// 版本1
Direction e = Direction.QIAN;
System.out.println(e);//public String toString()返回枚舉常量的名稱,它包含在聲明中
System.out.println("---------");
// 版本2
Direction2 e2 =Direction2.HOU;
System.out.println(e2);
System.out.println(e2.getName());
System.out.println("---------");
// 版本3
Direction3 e3 = Direction3.ZUO;
System.out.println(e3);
System.out.println(e3.getName());
e3.fun();
Direction3 d = Direction3.QIAN;
switch(d){
case QIAN:
System.out.println("你選了前");
break;
case HOU:
System.out.println("你選了後");
break;
case ZUO:
System.out.println("你選了左");
break;
case YOU:
System.out.println("你選了右");
break;
}
}
}
版本1
public enum Direction{
QIAN,HOU,ZUO,YOU;
}
版本2
public enum Direction2 {
QIAN("前"),HOU("後"),ZUO("左"),YOU("右");
private String name;
private Direction2(String name){
this.name=name;
}
public String getName(){
return name;
}
}
版本3
public enum Direction3 {
QIAN("前"){
@Override
public void fun() {
System.out.println("前");
}
},HOU("後"){
@Override
public void fun() {
// TODO Auto-generated method stub
System.out.println("後");
}
},ZUO("左"){
@Override
public void fun() {
// TODO Auto-generated method stub
System.out.println("左");
}
},YOU("右"){
@Override
public void fun() {
// TODO Auto-generated method stub
System.out.println("右");
}
};
private String name;
private Direction3(String name){
this.name=name;
}
public String getName(){
return name;
}
public abstract void fun();
}
4.注意事項
- 定義枚舉類要用關鍵字enum
- 所有枚舉類都是Enum的子類
- 枚舉類的第一行上必須是枚舉項,最後一個枚舉項後的分號是可以省略的,但是如果枚舉類有其他的東西,這個分號就不能省略。建議不要省略
- 枚舉類可以有構造器,但必須是private的,它默認的也是private的。枚舉項的用法比較特殊:枚舉(“”);
- 枚舉類也可以有抽象方法,但是枚舉項必須重寫該方法
- 枚舉在switch語句中的使用
public class Demo_Switch {
public static void main(String[] args) {
Direction3 d = Direction3.QIAN;
switch(d){
case QIAN:
System.out.println("你選了前");
break;
case HOU:
System.out.println("你選了後");
break;
case ZUO:
System.out.println("你選了左");
break;
case YOU:
System.out.println("你選了右");
break;
}
}
}
5.枚舉類中的幾個常見方法
public final int compareTo(E o)
比較此枚舉與指定對象的順序。
public final Stringname()
返回此枚舉常量的名稱,在其枚舉聲明中對其進行聲明。
public final intordinal()
返回枚舉常量的序數(它在枚舉聲明中的位置,其中初始常量序數爲零)。
public String toString()
返回枚舉常量的名稱,它包含在聲明中。可以重寫此方法,雖然一般來說沒有必要。當存在更加“程序員友好的”字符串形式時,應該使用枚舉類型重寫此方法。
public static <T extends Enum<T>> TvalueOf(Class<T> enumType,String name)
返回帶指定名稱的指定枚舉類型的枚舉常量。名稱必須與在此類型中聲明枚舉常量所用的標識符完全匹配。
values()
此方法雖然在JDK文檔中查找不到,但每個枚舉類都具有該方法,它遍歷枚舉類的所有枚舉值非常方便
public enum Direction2 {
QIAN("前"),HOU("後"),ZUO("左"),YOU("右");
private String name;
private Direction2(String name){
this.name=name;
}
public String getName(){
return name;
}
@Override
public String toString() {
// TODO Auto-generated method stub
return "I LOVE YOU";
}
}
public class EnumFunction {
public static void main(String[] args) {
// int compareTo(E o)
// public final int compareTo(E o)比較此枚舉與指定對象的順序。
Direction2 d21 = Direction2.QIAN;
Direction2 d22 = Direction2.HOU;
Direction2 d23 = Direction2.ZUO;
Direction2 d24 = Direction2.YOU;
System.out.println(d21.compareTo(d24));//-3
System.out.println(d24.compareTo(d21));//3
System.out.println("------------");
// String name()
// public final String name()返回此枚舉常量的名稱,在其枚舉聲明中對其進行聲明。
System.out.println(d21.name());
System.out.println(d22.name());
System.out.println(d23.name());
System.out.println(d24.name());
System.out.println("------------");
// int ordinal()
// public final int ordinal()返回枚舉常量的序數(它在枚舉聲明中的位置,其中初始常量序數爲零)。
System.out.println(d21.ordinal());//0
System.out.println(d22.ordinal());//1
System.out.println(d23.ordinal());//2
System.out.println(d24.ordinal());//3
System.out.println("------------");
// String toString()
// public String toString()返回枚舉常量的名稱,它包含在聲明中。
// 可以重寫此方法,雖然一般來說沒有必要。當存在更加“程序員友好的”字符串形式時,應該使用枚舉類型重寫此方法。
System.out.println(d21.toString());
System.out.println(d22.toString());
System.out.println(d23.toString());
System.out.println(d24.toString());
System.out.println("------------");
// <T> T valueOf(Class<T> type,String name)
// public static <T extends Enum<T>> T valueOf(Class<T> enumType,String name)
// 返回帶指定名稱的指定枚舉類型的枚舉常量。名稱必須與在此類型中聲明枚舉常量所用的標識符完全匹配。
Direction2 d = Direction2.valueOf(Direction2.class, "QIAN");
System.out.println(d.getName());
System.out.println("------------");
// values()
Direction2[] a = Direction2.values();
for (Direction2 d1 : a) {
System.out.println(d1.getName());
}
}
}
三.JDK7新特性
1.二進制字面量
JDK7開始,終於可以用二進制來表示整數(byte,short,int和long)。
使用二進制字面量的好處是,可以使代碼更容易被理解。只要在二進制數值前面加0b或者0B
int x = 0b110110
2.數字字面量可以出現下劃線
爲了增強對數值的閱讀性,如我們經常把數據用逗號分隔一樣。JDK7提供了_對數據分隔。
舉例:
int x = 100_1000;
注意事項:
- 不能出現在進制標識和數值之間
- 不能出現在數值開頭和結尾
- 不能出現在小數點旁邊
3.switch 語句可以用字符串
4.泛型簡化
5.異常的多個catch合併
6.try-with-resources 語句
try(必須是java.lang.AutoCloseable的子類對象){…}
好處:
- 資源自動釋放,不需要close()了
- 把需要關閉資源的部分都定義在這裏就ok了
- 主要是流體系的對象是這個接口的子類(看JDK7的API)