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 参考资料
以上都是个人整理资料部分,有问题欢迎大家留言!如需转载,请注明出处!