Java基礎課程-陣列-反射機制
1.上章回顧與預習檢查
1.1 上章回顧
- 介面與抽象類的區別?
- 什麼是內部類?
- 談談你對回撥函式的理解?
1.2 預習檢查
- 什麼是陣列?
- 陣列中是length屬性還是length()方法?
- 陣列的下標從幾開始?
- 什麼是反射?
2. 本章任務
- 解剖陣列;
- 陣列排序;
- 學生成績等級劃分;
- 楊輝三角;
- 解剖類;
3. 本章內容
- 陣列
- 反射機制
1.1 陣列
1.1.1 陣列概述
所謂陣列,就是相同資料型別的元素按一定順序排列的集合,就是把有限個型別相同的變數用一個名字命名,然後用編號區分他們的變數的集合,這個名字稱為陣列名,編號稱為下標。組成陣列的各個變數稱為陣列的分量,也稱為陣列的元素,有時也稱為下標變數。陣列是在程式設計中,為了處理方便, 把具有相同型別的若干變數按有序的形式組織起來的一種形式。這些按序排列的同類資料元素的集合稱為陣列。
歸納如下:
- 陣列是多個相同型別資料的資料結構,實現對這些資料的統一管理
- 陣列屬引用型別,陣列型資料是物件(object),陣列中的每個元素相當於該物件的成員變數
- 陣列中的元素可以是任何資料型別,包括基本型別和引用型別
1.1.2 一維陣列宣告
type var[] 或 type[] var;
程式碼示例1:
int a[];
int[] a1;
double b[];
Mydate []c;
注意:
Java語言中宣告陣列時不能指定其長度(陣列中元素的個數).
程式碼示例2:
int a[5]; //非法
1.1.3 一維陣列的建立
聲明瞭一個一維陣列,僅僅是給出了陣列名稱和元素的資料型別,要想真正使用陣列還必須為它記憶體空間,即建立陣列。在為陣列分配記憶體空間時,必須指明陣列的長度。建立陣列要用到運算子new.
程式碼示例3:
arrayName = new type[size]; 如:
page = new int[3];
建立一維陣列記憶體模式如下圖:
1.1.4 陣列元素的引用
定義並用運算子new為之分配空間後後,才可以引用陣列中的每個元素;
陣列元素的引用方式:arrayName[index]
- arrayName陣列的變數名稱
- index為陣列元素下標,可以是整型常量或整型表示式。如a[3] , b[i] , c[6*i];
- 陣列元素下標從0開始;長度為n的數組合法下標取值範圍: 0 ~ n-1;
備註:每個陣列都有一個屬性length指明它的長度
程式碼示例4:
//建立陣列
int arr[]=new int[4];
//陣列的length屬性
System.out.println(arr.length);
1.1.5 陣列元素的初始化
動態初始化
陣列定義與為陣列元素分配空間並賦值的操作分開進行。
程式碼示例5:int a[]; a = new int[3]; a[0] = 3; a[1] = 9; a[2] = 8; MyDate dates[]= new MyDate[3]; dates[0] = new MyDate(22, 7, 1964); dates[1] = new MyDate(1, 1, 2000); dates[2] = new MyDate(22, 12, 1964);
靜態初始化
在定義陣列的同時就為陣列元素分配空間並賦值。
程式碼示例6:int a[] = { 3, 9, 8}; MyDate dates[] = { new MyDate(22, 7, 1964), new MyDate(1, 1, 2000), new MyDate(22, 12, 1964) };
1.1.6 簡單資料型別陣列
簡單資料型別陣列的定義
在定義陣列的時候,系統會給這個陣列分配用於存放這個陣列的記憶體空間
如圖所示:int arr[];
簡單資料型別陣列的建立
在建立簡單資料型別的陣列的時候,系統會分配合適的空間用來存放該種資料型別資料的記憶體空間,並且將這個陣列的各個元素賦一個和陣列型別匹配的初值
如圖所示:arr=new int[10];
3. 簡單資料型別陣列的初始化
對於簡單資料型別的陣列,當對其進行初始化時,會將對應的值賦給對應的各個陣列元素
如圖所示:
arr[0]=1;
arr[1]=2;
arr[2]=3;
......
arr[9]=10;
基本資料型別陣列程式碼示例
public class Test {
public static void main(String args[]) {
//陣列宣告
int[] s;
//建立
s = new int[10];
for (int i = 0; i < 10; i++) {
//初始化
s[i] = 2 * i + 1;
//輸出結果
System.out.print(s[i]+",");
}
}
}
執行結果:
1,3,5,7,9,11,13,15,17,19,
備註:分析示例中陣列物件在記憶體中的變化
1.1.7 引用資料型別陣列
引用資料型別陣列的定義
引用型別陣列的定義和簡單型別資料型別陣列的定義並無二致
如圖所示:String arr[];
引用資料型別陣列的建立
引用資料型別陣列在建立的時候也是首先給陣列元素分配記憶體空間,然後給這些陣列元素一個預設的初始值nullarr=new String[10];
引用資料型別陣列的初始化
在進行引用資料型別陣列的初始化的時候,和簡單資料型別陣列的初始化有些不同,因為陣列本身是引用型別,而現在陣列元素也是引用型別,所以這個時候需要給陣列元素所引用的物件也分配記憶體空間。arr[0]=new String("one"); arr[1]=new String("two"); ... ... arr[9]=new String("ten");
陣列是引用型別,它的元素相當於類的成員變數,因此陣列一經分配空間,其中的每個元素也被按照成員變數同樣的方式被隱式初始化
引用資料型別陣列程式碼示例
/**
* 自定義日期類
*
* @author change
*
*/
class MyDate {
// 月中的天
private int day;
// 年中的月
private int month;
// 年
private int year;
/**
* 建構函式
*
* @param d
* @param m
* @param y
*/
public MyDate(int d, int m, int y) {
day = d;
month = m;
year = y;
}
/**
* 列印物件資訊
*/
public void display() {
System.out.println(year + "-" + month + "-" + day);
}
}
public class TestDemo {
public static void main(String args[]) {
//宣告陣列
MyDate[] m;
//建立陣列
m = new MyDate[5];
for (int i = 0; i < 5; i++) {
//陣列賦值
m[i] = new MyDate(i + 1, i + 1, 1990 + i);
//列印物件資訊
m[i].display();
}
}
}
執行結果:
1990-1-1
1991-2-2
1992-3-3
1993-4-4
1994-5-5
備註:分析示例中陣列物件在記憶體中的變化
案例1:解剖陣列
public class ArrDemo{
public static void main(String[] args) {
//陣列的建立
String arr[]=new String[5];
//陣列的初始化
arr[0]="唐僧";
arr[1]="悟空";
arr[2]="白龍馬";
arr[3]="八戒";
arr[4]="沙僧";
//陣列的具體的某個元素值
System.out.println("陣列的第四個元素:"+arr[3]);
//陣列的長度屬性
System.out.println("陣列的長度是:"+arr.length);
//遍歷陣列的第一種方式
for(int i=0;i<arr.length;i++){
System.out.print(arr[i]+",");
}
//換行 考慮上邊的輸出去掉最後一個逗號的問題
System.out.println();
//遍歷陣列的方式
for(int i=0;i<arr.length;i++){
if(i<arr.length-1){
System.out.print(arr[i]+",");
}else{
System.out.print(arr[i]);
}
}
//換行
System.out.println();
//遍歷陣列的第二種方式
for(String ar:arr){
System.out.print(ar+" ");
}
//思考第一種方式與第二種方式的區別
}
}
執行結果:
陣列的第四個元素:八戒
陣列的長度是:5
唐僧,悟空,白龍馬,八戒,沙僧,
唐僧,悟空,白龍馬,八戒,沙僧
唐僧 悟空 白龍馬 八戒 沙僧
1.1.8 一維陣列的應用
利用一維陣列來進行氣泡排序:
- 對幾個無序的數字進行排序,最常用的方法是所謂的氣泡排序法。這種方法每次比較兩個相鄰的數,將較小的放到前面,較大的放到後面,這樣就可以將這些數中的最大的找出來訪到最後,然後比較剩下的數,再在這些數中找出最大的來,直到所有的數字按照從小到大的順序排列
- 可以用一個一維陣列來存放這些需要進行排序的數字,然後對這個一維陣列進行排序
氣泡排序
案例2:氣泡排序public static void main(String[] args) { int[] a={1,3,5,75,1,6,9,8,5,2,2,1,4,2}; for(int i=0;i<a.length-1;i++){ for(int j=i;j<a.length-1;j++){ if(a[i]>a[j+1]){ int temp=a[i]; a[i]=a[j+1]; a[j+1]=temp; } } } for(int b:a){ System.out.println(b); } }
案例3: 從鍵盤讀入學生成績,找出最高分,並輸出學生成績等級。
等級說明:
成績>=最高分-10 等級為’A’
成績>=最高分-20 等級為’B’
成績>=最高分-30 等級為’C’
其餘 等級為’D’
提示:先讀入學生人數,根據人數建立int陣列,存放學生成績。
/**
* 此操作都是在正確輸入引數的情況測試的!
* @author change
*
*/
public class StudentDemo {
public static void main(String[] args) {
// 掃描物件
Scanner scanner = new Scanner(System.in);
//提示輸入錄入學生的人數
System.out.println("請輸入錄入學生的人數");
//掃描輸入的學生的人數
String snum = scanner.nextLine();
//轉換成數字
int num = Integer.parseInt(snum);
//建立陣列
int arr[]=new int[num];
System.out.println("請輸入"+num+"位學生的成績");
//遍歷陣列
for(int i=0;i<num;i++){
//接受輸入的學生成績
String score = scanner.nextLine();
//初始化賦值
arr[i]=Integer.parseInt(score);
}
//陣列排序
Arrays.sort(arr);
//獲取最高的成績5
int maxScore = arr[num-1];
System.out.println("學生等級劃分結果");
//遍歷陣列
for(int i=0;i<num;i++){
if(arr[i]>maxScore-10){
System.out.println(arr[i]+"等級為A");
}else if(arr[i]>maxScore-20){
System.out.println(arr[i]+"等級為B");
}else if(arr[i]>maxScore-30){
System.out.println(arr[i]+"等級為C");
}else{
System.out.println(arr[i]+"等級為D");
}
}
}
}
測試執行:
請輸入錄入學生的人數
5
請輸入5位學生的成績
98
88
85
75
65
學生等級劃分結果
65等級為D
75等級為C
85等級為B
88等級為B
98等級為A
1.1.9 多維陣列
Java語言中,多維陣列被看作是陣列的陣列。例如二維陣列被看作是一個特殊的一維陣列,其每個元素又是一個一維陣列。下面主要以二維陣列為例進行說明。
二維陣列的宣告方式:
type var[][] 或 type[][] var; 其中type是定義陣列包含元素的資料型別,它可以是Java支援的任何一種資料型別, 可以是基本資料型別,也可以是引用資料型別; var是陣列的名字,可以是任何一個合法的識別符號。
二維陣列舉例:
int a[][] = {{1,2},{3,4,0,9},{5,6,7}};
圖解說明如下:
注意事項
Java中二維陣列的宣告和初始化應按從高維到低維的順序進行
int t [][] = new int [4][]; t[0] = new int[5]; t[1] = new int[5]; int t1[][] = new int [][4]; //非法
Java中二維陣列不必須是規則矩陣形式
int[][] tt = new int[4][]; tt[0] = new int[2]; tt[1] = new int[4]; tt[2] = new int[6]; tt[3] = new int[8]; int tt[][] = new int[4][5];
二維陣列初始
靜態初始化
int intArray[][] = {{1,2},{2,3},{3,4,5}}; int intArray1[3][2] = {{1,2},{2,3},{4,5}}; //非法定義
動態初始化
int a[][] = new int[4][5]; int b[][] = new int[3][] b[0] = new int[2]; b[1] = new int[3]; b[2] = new int[5];
二維陣列示例:
public class ArrsDemo { public static void main(String[] args) { //建立二維陣列 int[][] a = new int[3][]; //遍歷陣列 for (int i = 0; i < a.length; i++) { // 建立一維陣列的陣列元素 a[i] = new int[i + 2]; //元素賦值 for (int j = 0; j < a[i].length; j++) { a[i][j] = i + j; } } // 列印 for (int i = 0; i < a.length; i++) {// 代表的是行 for (int j = 0; j < a[i].length; j++) {// 代表行中的元素:即就是幾列。 System.out.print(a[i][j] + " "); } System.out.println(); } } }
執行結果:
0 1 1 2 3 2 3 4 5
案例4:使用二維陣列列印一個 10 行楊輝三角
分析:
- 第一行有 1 個元素, 第 n 行有 n 個元素
- 每一行的第一個元素和最後一個元素都是 1
從第三行開始, 對於非第一個元素和最後一個元素的元素,其它元素的值的公式如下:
yanghui[i][j] = yanghui[i-1][j-1] + yanghui[i-1][j];
程式碼實現:
/*楊輝三角實現*/
public class YangHuiDemo {
public static void main(String[] args) {
//定義二維陣列
int yanghui[][] =new int[10][];
//遍歷二維陣列
for(int row=0;row<yanghui.length;row++){
//每行有n個元素
yanghui[row]=new int[row+1]; //第一行有一個元素,依次類推.
//位每行元素賦值
for(int col=0;col<yanghui[row].length;col++){
if(row==col||col==0){
yanghui[row][col]=1;
}
else{
yanghui[row][col]=yanghui[row-1][col-1]+yanghui[row-1][col];
}
//遍歷元素 \t水平製表符
System.out.print(yanghui[row][col]+"\t");
}
System.out.println();
}
}
}
執行結果:
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1
1 7 21 35 35 21 7 1
1 8 28 56 70 56 28 8 1
1 9 36 84 126 126 84 36 9 1
擴充套件思考:怎麼實現規則的輸出?
1.1.10 陣列中常見的異常
陣列腳標越界異常(ArrayIndexOutOfBoundsException)
示例程式碼:public class ArrException { public static void main(String[] args) { int[] arr = new int[2]; System.out.println(arr[2]); } }
執行結果:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 2 at arr.ArrException.main(ArrException.java:7)
空指標異常(NullPointerException)
示例程式碼:public class ArrException { public static void main(String[] args) { String[] arr = null; System.out.println(arr[0]); } }
執行結果:
Exception in thread "main" java.lang.NullPointerException at arr.ArrException.main(ArrException.java:7)
1.2 反射
1.2.1 反射介紹
JAVA反射機制是在執行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個物件,都能夠呼叫它的任意一個方法和屬性;這種動態獲取的資訊以及動態呼叫物件的方法的功能稱為java語言的反射機制
簡單的說:一個類有多個組成部分,例如:成員變數,方法,構造方法等。反射就是載入類,並解剖出類的各個組成部分
Java反射機制主要提供了以下功能:
- 在執行時判斷任意一個物件所屬的類;
- 在執行時構造任意一個類的物件;
- 在執行時判斷任意一個類所具有的成員變數和方法;
- 在執行時呼叫任意一個物件的方法;生成動態代理
備註:程式執行時,允許改變程式結構或變數型別,這種語言稱為動態語言”。從這個觀點看,Perl,Python,Ruby是動態語言,C++,Java,C#不是動態語言
1.2.2 反射的API介紹
Java反射所需要的類並不多,主要有java.lang.Class類和java.lang.reflect包中的Field、Constructor、Method、Array類。
注意:Class類是Java反射的起源,針對任何一個你想探勘的類,只有先為它產生一個Class類的物件,接下來才能通過Class物件獲取其他想要的資訊。
- Class類
- JVM為每種型別管理著一個獨一無二的Class物件—每個類(型)都有一個Class物件
- Java程式執行過程中,當需要建立某個類的例項時,JVM首先檢查所要載入的類對應的Class物件是否已經存在。如果還不存在,JVM就會根據類名查詢對應的位元組碼檔案並載入,接著建立對應的Class物件,最後才創建出這個類的例項。
- Java的基本資料型別都對應著一個Class物件;關鍵字void也都對應著一個Class物件;每個陣列屬性也被對映為 Class 物件,所有具有相同型別和維數的陣列都共享該 Class 物件
因此,執行中的類或介面在JVM中都會有一個對應的Class物件存在,它儲存了對應類或介面的型別資訊。要想獲取類或介面的相應資訊,需要先獲取這個Class物件。
- 載入類
- Java中有一個Class類用於代表某一個類的位元組碼
- Class類即然代表某個類的位元組碼,它當然就要提供載入某個類位元組碼的方法:forName()。forName方法用於載入某個類的位元組碼到記憶體中,並使用class物件進行封裝
- 另外兩種得到class物件的方式
類名.class: Manager.class; int.class; double[].class;
物件.getClass()
解剖類
Class物件提供瞭如下常用方法
Public Constructor getConstructor(Class<?>... parameterTypes) Public Method getMethod(String name, Class<?>... parameterTypes) Public Field getField(String name) public public Constructor getDeclaredConstructor(Class... parameterTypes) public Method getDeclaredMethod(String name,Class... parameterTypes) public Field getDeclaredField(String name)
這些方法分別用於從類中解剖出建構函式、方法和成員變數(屬性)。解剖出的成員分別使用Constructor、 Method 、 Field 物件表示。
1.2.3 反射案例
**案例5:**Java反射案例
class Person {
private int age;
private String name;
public Person() {
}
public Person(int age, String name) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
interface ActionInterface {
public void walk(int m);
}
class SuperMan extends Person implements ActionInterface {
private boolean blueBriefs;
public void fly() {
System.out.println("超人會飛耶~~");
}
public boolean isBlueBriefs() {
return blueBriefs;
}
public void setBlueBriefs(boolean blueBriefs) {
this.blueBriefs = blueBriefs;
}
@Override
public void walk(int m) {
System.out.println("超人會走耶~~走了" + m + "米就走不動了!");
}
}
示例1:通過Java反射機制得到類的包名和類名
public static void main(String[] args){
Person person = new Person();
System.out.println("包名:" + person.getClass().getPackage().getName());
System.out.println("完整類名:" + person.getClass().getName());
}
示例2:驗證所有的類都是Class類的例項物件
public static void main(String[] args){
// 定義兩個型別都未知的Class,設定初值為null,看看如何給它們賦值成Person類
Class<?> class1 = null;
Class<?> class2 = null;
// 寫法1,可能丟擲 ClassNotFoundException 異常,多用這個寫法
try {
class1 = Class.forName("shuang.com.demo.Person");//包名要寫正確,不然拋異常
System.out.println("寫法1,包名:" + class1.getPackage().getName() + " , 完整類名:" + class1.getName());
} catch (ClassNotFoundException e) {
System.out.println("寫法1找不到類");
}
// 寫法2
class2 = Person.class;
System.out.println("寫法2,包名:" + class2.getPackage().getName() + " , 完整類名:" + class2.getName());
}
示例3:通過Java反射機制,用 Class 建立類物件,這也就是反射存在的意義所在
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException{
Class<?> class1 = null;
class1 = Class.forName("shuang.com.demo.Person");
// 由於這裡不能帶引數,所以你要例項化的這個類Person,一定要有無參建構函式
Person person = (Person) class1.newInstance();
person.setName("xiaoming");
person.setAge(20);
System.out.println(person.getName() + " , " + person.getAge());
}
示例4:通過Java反射機制得到一個類的建構函式,並實現建立帶參例項物件
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException,IllegalArgumentException, InvocationTargetException{
Class<?> class1 = null;
Person person1 = null;
Person person2 = null;
class1 = Class.forName("shuang.com.demo.Person");
Constructor<?>[] constructors = class1.getConstructors();
person1 = (Person) constructors[0].newInstance();
person1.setName("xiaoming");
person1.setAge(20);
System.out.println(person1.getName() + " , " + person1.getAge());
person2 = (Person) constructors[1].newInstance(21, "xiaohong");
System.out.println(person2.getName() + " , " + person2.getAge());
}
示例5:通過Java反射機制操作成員變數, set 和 get
public static void main(String args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException, SecurityException{
Class<?> class1 = Class.forName("shuang.com.demo.Person");
Object obj = class1.newInstance();
Field personNameField = class1.getDeclaredField("name");
personNameField.setAccessible(true); // 取消訪問檢查
personNameField.set(obj, "小虎");
System.out.println("修改屬性之後得到屬性變數的值:" + personNameField.get(obj));
}
示例6:通過Java反射機制得到類的一些屬性:繼承的介面、父類、函式資訊、成員資訊、型別等
public static void main(String[] args) throws ClassNotFoundException{
Class<?> class1 = Class.forName("shuang.com.demo.SuperMan");
// 取得父類名稱
Class<?> superclass = class1.getSuperclass();
System.out.println("SuperMan類的父類名:" + superclass.getName());
Field[] fields = class1.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
System.out.println("類中的成員" + i + ": " + fields[i]);
}
// 取得類方法
Method[] methods = class1.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
System.out.println("取得SuperMan類的方法" + i + ":");
System.out.println("函式名:" + methods[i].getName());
System.out.println("函式返回型別:" + methods[i].getReturnType());
System.out.println("函式訪問修飾符:" + Modifier.toString(methods[i].getModifiers()));
System.out.println("函式程式碼寫法: " + methods[i]);
}
// 取得類實現的介面,因為介面類也屬於Class,所以得到介面中的方法也是一樣的方法得到哈
Class<?> interfaces[] = class1.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
System.out.println("實現的介面類名: " + interfaces[i].getName());
}
}
示例7:通過Java反射機制呼叫類方法
public static void main(String[] args)throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException,InstantiationException, InvocationTargetException{
Class<?> class1 = Class.forName("shuang.com.demo.SuperMan");
System.out.println("呼叫無參方法fly():");
Method method = class1.getMethod("fly");
method.invoke(class1.newInstance());
System.out.println("呼叫有參方法walk(int m):");
method = class1.getMethod("walk", int.class);
method.invoke(class1.newInstance(), 100);
}
5 總結
- 陣列的宣告及使用
- 瞭解反射機制
6 預習任務
- Object類
- String類
- StringBuffer與StringBuilder類
- 日期處理的類
- Math與Random
7 參考資料
以上都是個人整理資料部分,有問題歡迎大家留言!如需轉載,請註明出處!