--------------------------------------------------------------------------------------------------------------------------------------
//利用反射的目光,重新学习数组,
案例:
数组是类吗?
一个数组是一个对象吗?
数组与类的关系是什么?
思路:
int[] arr = {1,2,3,4,5}; //数组类本身就是一个普通类.但普通类却不一定是数组类!
System.out.println("" instanceof Object); //结果true
System.out.println((new Person()) instanceof Object); //结果true
System.out.println(arr instanceof Object); //结果true
System.out.println(arr.getClass().getSuperclass().getName()); //结果java.lang.Object
System.out.println(arr.getClass()); //结果class [I (这是Java中的"数组类"的独有的结构)
System.out.println(arr.getClass().isArray()); //结果true (是一个类,也是一个数组类)
System.out.println(Array.class); //结果class java.lang.reflect.Array (这是Java中的"普通类"的结构)
System.out.println(Array.class.isArray()); 结果false (是一个类,但不是一个数组类)
//利用反射的目光,重新学习数组,
案例:
数组是类吗?
一个数组是一个对象吗?
数组与类的关系是什么?
思路:
int[] arr = {1,2,3,4,5}; //数组类本身就是一个普通类.但普通类却不一定是数组类!
System.out.println("" instanceof Object); //结果true
System.out.println((new Person()) instanceof Object); //结果true
System.out.println(arr instanceof Object); //结果true
System.out.println(arr.getClass().getSuperclass().getName()); //结果java.lang.Object
System.out.println(arr.getClass()); //结果class [I (这是Java中的"数组类"的独有的结构)
System.out.println(arr.getClass().isArray()); //结果true (是一个类,也是一个数组类)
System.out.println(Array.class); //结果class java.lang.reflect.Array (这是Java中的"普通类"的结构)
System.out.println(Array.class.isArray()); 结果false (是一个类,但不是一个数组类)
由此可见:
1.数组这种数据类型,都是Java中的类类型,是一种特殊的类类型(数组类).
2.有多少种数据类型和维数,就可以有多少个数组类,所以数组类的个数是无数多个(太多了,API中就不写了).
3.Array和Arrays都是数组工具类,没有构造方法,不能被实例化,本身只有静态方法.
1.数组这种数据类型,都是Java中的类类型,是一种特殊的类类型(数组类).
2.有多少种数据类型和维数,就可以有多少个数组类,所以数组类的个数是无数多个(太多了,API中就不写了).
3.Array和Arrays都是数组工具类,没有构造方法,不能被实例化,本身只有静态方法.
结论:
Java中总共有2种类,(普通类 + 数组类):
1.int[],Person[],Object[]都是数组类.
2.Object,Class,Array都是普通类.
3.数组类的结构 = 数组的维数 + 数组的数据类型,例如 class [I
Java中总共有2种类,(普通类 + 数组类):
1.int[],Person[],Object[]都是数组类.
2.Object,Class,Array都是普通类.
3.数组类的结构 = 数组的维数 + 数组的数据类型,例如 class [I
更远:
1.数组既然是一个类,数组类当然也可以有子类,数组类当然也遵循多态的使用方式.
2.数组类与数组子类的多态写法,
Person[] p = new Student[]{}; //这样写,是完全正确的.
3.在寻找数组类的继承关系时,出现奇怪的现象:
System.out.println(Person[].class.getSuperclass().getName()); //java.lang.Object
System.out.println(Student[].class.getSuperclass().getName()); //java.lang.Object <---(寻找父类)
System.out.println(Student.class.getSuperclass().getName()); //Person
System.out.println(new Student[ 5 ] instanceof Person[ ]); //true <---(寻找父类)
寻找Student[]类的父类,有时是Object,有时是Person[],数组类的继承体系,还是有点尴尬的!
可见,数组的继承是"依赖类"继承,不能自定义,不能人工干预,是自然而然存在的.
---------------------------------------------------------------------------------------------------------------------------------------
理解数组类的多态------->就能理解数组在内存中的数据结构:
1.父类引用,可以指向子类对象,子类引用,不可以指向父类对象,数组间的引用仍然遵循多态的原则,
2.数组是一个容器,而且是一个很特殊的容器(这个容器与容器中的数据类型,进行了"不可更改"的强强绑定),只能存放特定类型的数据!
3.如果想在数组中存放其它类型的数据,只能再去new一个其它类型的数组,没有其它任何办法,可以改变原数组的数据类型!
1.数组既然是一个类,数组类当然也可以有子类,数组类当然也遵循多态的使用方式.
2.数组类与数组子类的多态写法,
Person[] p = new Student[]{}; //这样写,是完全正确的.
3.在寻找数组类的继承关系时,出现奇怪的现象:
System.out.println(Person[].class.getSuperclass().getName()); //java.lang.Object
System.out.println(Student[].class.getSuperclass().getName()); //java.lang.Object <---(寻找父类)
System.out.println(Student.class.getSuperclass().getName()); //Person
System.out.println(new Student[ 5 ] instanceof Person[ ]); //true <---(寻找父类)
寻找Student[]类的父类,有时是Object,有时是Person[],数组类的继承体系,还是有点尴尬的!
可见,数组的继承是"依赖类"继承,不能自定义,不能人工干预,是自然而然存在的.
---------------------------------------------------------------------------------------------------------------------------------------
理解数组类的多态------->就能理解数组在内存中的数据结构:
1.父类引用,可以指向子类对象,子类引用,不可以指向父类对象,数组间的引用仍然遵循多态的原则,
2.数组是一个容器,而且是一个很特殊的容器(这个容器与容器中的数据类型,进行了"不可更改"的强强绑定),只能存放特定类型的数据!
3.如果想在数组中存放其它类型的数据,只能再去new一个其它类型的数组,没有其它任何办法,可以改变原数组的数据类型!
突破多态机制的限制:
//利用反射的目光,重新学习多态,
1.子类拥有自己的和从父类继承的,成员变量和成员方法,private的除外,
2.不可调用重写的子类中的成员变量,通过强制转型,可以调用,
3.不可调用重写的父类中的成员方法,通过super关键字,可以调用,
注意:
父类引用指向子类对象,所得到的这个中间层的对象,本质还是一个子类对象,(反射验证)
Java中的普通类和数组类的转型:
1.普通类和数组类,都支持强制转型,语法是一样的.
2.普通类和数组类,都支持暴力转型,语法是一样的.
注意:
强制转型都不能改变对象所属的类,暴力转型(新new对象)可以改变.
--------------------------------------------------------------------------------------------------------------------------------------------------------------
多态中的强制转型与暴力转型:
类继承模型:
Person
|--Teacher
|--Student
Person
|--Teacher
|--Student
多态中的强制转型:
//利用反射的目光,重新学习多态中的----强制转型,
案例:
1--Person p1 = (Person)(new Student()); //正确,向上转型成功.
2--Student s1 = (Student)(new Person()); //运行时异常,向下转型失败.
3--Student s2 = new Person(); //编译时异常,相当于向下转型.
4--Person p2 = new Student(); //正确,相当于向上转型.
5--Student s3 = (Student)p2; //正确,可以转成真正的Student
6--Person p3 = (Person)p2; //正确,但是转不成真正的Person
注意,第1句和第4句和第6句,本质上完全一样,在多态的应用上,没有任何的区别.
//利用反射的目光,重新学习多态中的----强制转型,
案例:
1--Person p1 = (Person)(new Student()); //正确,向上转型成功.
2--Student s1 = (Student)(new Person()); //运行时异常,向下转型失败.
3--Student s2 = new Person(); //编译时异常,相当于向下转型.
4--Person p2 = new Student(); //正确,相当于向上转型.
5--Student s3 = (Student)p2; //正确,可以转成真正的Student
6--Person p3 = (Person)p2; //正确,但是转不成真正的Person
注意,第1句和第4句和第6句,本质上完全一样,在多态的应用上,没有任何的区别.
多态中的暴力转型:
//利用反射的目光,重新学习多态中的----暴力转型,
案例:
Person p = new Student();
p = new Teacher(); //普通类暴力转型,类型转为Teacher
Person[ ] ps = new Student[ 5 ];
ps = new Teacher[ 6 ]; //数组类暴力转型,类型转为Teacher[ ]
Person[ ] ps2 = new Student[ 5 ];
ps2[ 0 ] = new Teacher(); //运行时报错,数组并没发生任何转型.
//利用反射的目光,重新学习多态中的----暴力转型,
案例:
Person p = new Student();
p = new Teacher(); //普通类暴力转型,类型转为Teacher
Person[ ] ps = new Student[ 5 ];
ps = new Teacher[ 6 ]; //数组类暴力转型,类型转为Teacher[ ]
Person[ ] ps2 = new Student[ 5 ];
ps2[ 0 ] = new Teacher(); //运行时报错,数组并没发生任何转型.
数组的经典的报错方式:
java.lang.RuntimeException
|--java.lang.ArrayStoreException 数组错误类型存储
|--java.lang.IndexOutOfBoundsException
|--ArrayIndexOutOfBoundsException 数组下标越界
|--StringIndexOutOfBoundsException
---------------------------------------------------------------------------------------------------------------------------------------
以下2种情况,可以使用增强的for循环:
1.数组
2.实现Iterable接口的对象
java.lang.RuntimeException
|--java.lang.ArrayStoreException 数组错误类型存储
|--java.lang.IndexOutOfBoundsException
|--ArrayIndexOutOfBoundsException 数组下标越界
|--StringIndexOutOfBoundsException
---------------------------------------------------------------------------------------------------------------------------------------
以下2种情况,可以使用增强的for循环:
1.数组
2.实现Iterable接口的对象
理解数组和集合的迭代器:
1.使用这个迭代器,只能删查,不能增改,(因为迭代器中没有实现这样的方法)
2.增强for循环,在底层,都是调用的这个迭代器,
3.数组的迭代器是隐式的,只有增强for循环才能调用数组中的隐式的迭代器,反射也无法找到,程序员无法直接调用,
1.使用这个迭代器,只能删查,不能增改,(因为迭代器中没有实现这样的方法)
2.增强for循环,在底层,都是调用的这个迭代器,
3.数组的迭代器是隐式的,只有增强for循环才能调用数组中的隐式的迭代器,反射也无法找到,程序员无法直接调用,
Iterator<E>接口中的方法:
boolean hasNext()
是否还有下一个元素
E next()
步进迭代器的指针,并返回下一个元素,
如果没有元素可迭代,抛出NoSuchElementException,(是一个RuntimeException)
void remove()
只能移除next方法所返回的那个元素,依赖于next方法而存在,每次调用next只能调用一次此方法,
1.如果迭代器不支持remove操作,抛出UnsupportedOperationException,(是一个RuntimeException)
2.如果没有调用next方法,或者重复调用了remove方法,抛出IllegalStateException,(是一个RuntimeException)
boolean hasNext()
是否还有下一个元素
E next()
步进迭代器的指针,并返回下一个元素,
如果没有元素可迭代,抛出NoSuchElementException,(是一个RuntimeException)
void remove()
只能移除next方法所返回的那个元素,依赖于next方法而存在,每次调用next只能调用一次此方法,
1.如果迭代器不支持remove操作,抛出UnsupportedOperationException,(是一个RuntimeException)
2.如果没有调用next方法,或者重复调用了remove方法,抛出IllegalStateException,(是一个RuntimeException)
Iterator接口与Enumeration接口的关系:
1.这2个接口中的功能,是重复的,都是用来遍历集合,
2.Enumeration是JDK1.0中的接口,Iterator是JDK1.2中的接口,此外,Iterator接口添加了一个可选的remove()方法,
1.这2个接口中的功能,是重复的,都是用来遍历集合,
2.Enumeration是JDK1.0中的接口,Iterator是JDK1.2中的接口,此外,Iterator接口添加了一个可选的remove()方法,
Enumeration接口中的方法:
boolean hasMoreElements()
是否还有下一个元素
E nextElement()
步进迭代器的指针,并返回下一个元素,
如果没有元素可迭代,抛出NoSuchElementException,(是一个RuntimeException)
---------------------------------------------------------------------------------------------------------------------------------------
数组与int类型的数关系:
1.对象在内存中的地址,使用8个16进制数,占用4个字节,也就是一个int类型的数.
2.数组中至少有1个int类型的数,用来存储堆内存中的地址.
3.数组一般同时存在于栈和堆中的,栈中的局部变量,就是1个int类型的数.
boolean hasMoreElements()
是否还有下一个元素
E nextElement()
步进迭代器的指针,并返回下一个元素,
如果没有元素可迭代,抛出NoSuchElementException,(是一个RuntimeException)
---------------------------------------------------------------------------------------------------------------------------------------
数组与int类型的数关系:
1.对象在内存中的地址,使用8个16进制数,占用4个字节,也就是一个int类型的数.
2.数组中至少有1个int类型的数,用来存储堆内存中的地址.
3.数组一般同时存在于栈和堆中的,栈中的局部变量,就是1个int类型的数.
结论:
在数组与对象的相互赋值的过程中,一定要注意,数组在内存中的"数据结构"要相互匹配,
1."基本类型的数组"和"对象类型的数组",在内存中的数据结构,是不一样的,要区别开来!
2.(栈中的)局部变量占1层,数组的维数各占1层,基本数据类型不占层(例如int[][],共占3层)
3.(栈中的)局部变量占1层,数组的维数各占1层,对象类型占1层(例如Object[][],共占4层)
在数组与对象的相互赋值的过程中,一定要注意,数组在内存中的"数据结构"要相互匹配,
1."基本类型的数组"和"对象类型的数组",在内存中的数据结构,是不一样的,要区别开来!
2.(栈中的)局部变量占1层,数组的维数各占1层,基本数据类型不占层(例如int[][],共占3层)
3.(栈中的)局部变量占1层,数组的维数各占1层,对象类型占1层(例如Object[][],共占4层)
二维数组和嵌套数组的比较:
Object[ ] obj = new Object[]{new String[]{"111","222"},new String[]{"333","444","555"}}; //Object比较特殊,还有多态的概念
System.out.println(((String[])obj[ 1 ])[ 2 ]); //嵌套数组,还是一维数组
String[ ][ ] str = {{"0000"}}; //这是二维数组
System.out.println(str[ 0 ][ 0 ]);
Object[ ] obj = new Object[]{new String[]{"111","222"},new String[]{"333","444","555"}}; //Object比较特殊,还有多态的概念
System.out.println(((String[])obj[ 1 ])[ 2 ]); //嵌套数组,还是一维数组
String[ ][ ] str = {{"0000"}}; //这是二维数组
System.out.println(str[ 0 ][ 0 ]);
上面的多层嵌套数组,虚拟机只会认为是一维数组,为什么?
在虚拟机看来,多层嵌套数组与多维数组,又有什么本质的区别呢?虚拟机是凭什么认定了就是一个嵌套数组而不是多维数组呢?
1.还是取决于数组在内存中的数据结构,数组在内存中使用一种链式引用的数据结构(实际父类对象与子类对象,也是链式结构).
2.(栈中)变量-->int-->Object-->int-->Object-->int-->String,这种就是嵌套一维数组(由于多态的限制,只有Object才能引用数组00).
3.(栈中)变量-->int-->int-->int-->String,这种就是三维数组.
在虚拟机看来,多层嵌套数组与多维数组,又有什么本质的区别呢?虚拟机是凭什么认定了就是一个嵌套数组而不是多维数组呢?
1.还是取决于数组在内存中的数据结构,数组在内存中使用一种链式引用的数据结构(实际父类对象与子类对象,也是链式结构).
2.(栈中)变量-->int-->Object-->int-->Object-->int-->String,这种就是嵌套一维数组(由于多态的限制,只有Object才能引用数组00).
3.(栈中)变量-->int-->int-->int-->String,这种就是三维数组.
嵌套数组与Object类的关系:
1.只有Object类型的类,才有可能产生嵌套数组,因为只有Object类型才能引用数组类型,这是多态的概念!!!
2.经过试验,只有Object[]才能引用嵌套数组,只有Object[]能嵌套无数层,其它对象类型只能嵌套1层(还是因为多态).
3.只有连续出现的int,虚拟机认为是多维数组,int与Object交替出现,虚拟机认为那还是一维数组.
---------------------------------------------------------------------------------------------------------------------------------------
数组的初始化问题:
1.Object[] obj = new Object[]{}; //空的{}代表数组的长度为0
2.左边,不能写维数,因为左边的变量存放在栈中.
3.右边,没有{}必须写维数,有{}必须不能写维数
1.只有Object类型的类,才有可能产生嵌套数组,因为只有Object类型才能引用数组类型,这是多态的概念!!!
2.经过试验,只有Object[]才能引用嵌套数组,只有Object[]能嵌套无数层,其它对象类型只能嵌套1层(还是因为多态).
3.只有连续出现的int,虚拟机认为是多维数组,int与Object交替出现,虚拟机认为那还是一维数组.
---------------------------------------------------------------------------------------------------------------------------------------
数组的初始化问题:
1.Object[] obj = new Object[]{}; //空的{}代表数组的长度为0
2.左边,不能写维数,因为左边的变量存放在栈中.
3.右边,没有{}必须写维数,有{}必须不能写维数
案例:
int[] arr = new int[ 2 ]; //这句没错
arr = null; //只在栈中有,堆中还没有
System.out.println(arr[ 0 ]); //抛出NullPointerException
int[][] arr2 = new int[ 5 ][ ]; //正确
int[][] arr3 = new int[ ][ 5 ]; //报错
int[] arr = new int[ 2 ]; //这句没错
arr = null; //只在栈中有,堆中还没有
System.out.println(arr[ 0 ]); //抛出NullPointerException
int[][] arr2 = new int[ 5 ][ ]; //正确
int[][] arr3 = new int[ ][ 5 ]; //报错
注意:
当只初始化了数组的维数,而没有给数组赋具体的值时,数组存在堆中,堆中都会有默认值,
1.对于基本类型的数组,数组中的默认值为0或false,
2.对于引用类型的数组,数组中的默认值为null,此时,可以把null赋给任何的引用类型,而不会报错.
当只初始化了数组的维数,而没有给数组赋具体的值时,数组存在堆中,堆中都会有默认值,
1.对于基本类型的数组,数组中的默认值为0或false,
2.对于引用类型的数组,数组中的默认值为null,此时,可以把null赋给任何的引用类型,而不会报错.
案例:
Student[] studs = new Student[5];
System.out.println(studs[0]); //结果null
double[] doubs = new double[5];
System.out.println(doubs[0]); //结果0
Student[] studs = new Student[5];
System.out.println(studs[0]); //结果null
double[] doubs = new double[5];
System.out.println(doubs[0]); //结果0
数组的特点:
1.自动编号,长度固定,类型固定
2.元素有序,可以重复
3.跟C语言不同,在内存中,是树形结构,而不是矩阵结构
1.自动编号,长度固定,类型固定
2.元素有序,可以重复
3.跟C语言不同,在内存中,是树形结构,而不是矩阵结构
案例:
直接打印一个,在堆内存中的数组
System.out.println(arr);
结果格式为,[ + 数据类型 +内存地址
-------------------------------------------------------------------------
数组----求最值:
直接打印一个,在堆内存中的数组
System.out.println(arr);
结果格式为,[ + 数据类型 +内存地址
-------------------------------------------------------------------------
数组----求最值:
案例:
求最大值和最小值.
思路:
可以使用2种方式来完成:
1.定义一个临时变量,存储最值
2.定义一个临时变量,存储最值的角标
答案:
源码,day04,ArrayTest.java
-------------------------------------------------------------------------------------
数组----选择排序:
求最大值和最小值.
思路:
可以使用2种方式来完成:
1.定义一个临时变量,存储最值
2.定义一个临时变量,存储最值的角标
答案:
源码,day04,ArrayTest.java
-------------------------------------------------------------------------------------
数组----选择排序:
选择排序的特点:
1.选择排序像炮轰山,每排一次,最值都排在最左边.
2.排到最后,将剩下2个元素,就是结尾的那2个元素.
1.选择排序像炮轰山,每排一次,最值都排在最左边.
2.排到最后,将剩下2个元素,就是结尾的那2个元素.
选择排序的实现:
static void selectSort(int[] arr)
{
for (int i = 0; i < arr.length - 1; i++)
{
for (int j = i + 1; j < arr.length; j++)
{
if (arr[i] > arr[j]) //只需更换一处,便能实现逆序.
{
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
}
static void selectSort(int[] arr)
{
for (int i = 0; i < arr.length - 1; i++)
{
for (int j = i + 1; j < arr.length; j++)
{
if (arr[i] > arr[j]) //只需更换一处,便能实现逆序.
{
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
}
补充:
使用泛型重写数组中的swap方法.
static <T> void swap(T[ ] arr,int a,int b) //T[ ]在编译后,将转换为Object[ ]
{
T temp = arr[a]; //把 T 换成Object没任何区别
arr[a] = arr[b]; //泛型的意义,在于对泛型方法返回值的自动转换
arr[b] = temp; //泛型,在其它地方,基本上没用!
}
public static void main(String[] args)
{
Integer[] arr = {111,222,333,444,555};
System.out.println(Arrays.asList(arr));
swap(arr,3,4);
System.out.println(Arrays.asList(arr));
}
注意:
必须使用引用类型的数组,才不违反数组的多态机制!
-------------------------------------------------------------------------------------
数组----冒泡排序:
使用泛型重写数组中的swap方法.
static <T> void swap(T[ ] arr,int a,int b) //T[ ]在编译后,将转换为Object[ ]
{
T temp = arr[a]; //把 T 换成Object没任何区别
arr[a] = arr[b]; //泛型的意义,在于对泛型方法返回值的自动转换
arr[b] = temp; //泛型,在其它地方,基本上没用!
}
public static void main(String[] args)
{
Integer[] arr = {111,222,333,444,555};
System.out.println(Arrays.asList(arr));
swap(arr,3,4);
System.out.println(Arrays.asList(arr));
}
注意:
必须使用引用类型的数组,才不违反数组的多态机制!
-------------------------------------------------------------------------------------
数组----冒泡排序:
冒泡排序的特点:
1.选择排序像推土机,每排一次,最值都排在最右边.
2.排到最后,将剩下2个元素,就是开头的那2个元素.
1.选择排序像推土机,每排一次,最值都排在最右边.
2.排到最后,将剩下2个元素,就是开头的那2个元素.
冒泡排序的实现:
static void bubbleSort(int[] arr)
{
for (int i = arr.length - 1; i > 0; i--)
{
for (int j = 0; j < i; j++)
{
if (arr[j] > arr[j + 1]) //只需更换一处,便能实现逆序.
{
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
------------------------------------------------------------------------------------
数组----二分法查找:
static void bubbleSort(int[] arr)
{
for (int i = arr.length - 1; i > 0; i--)
{
for (int j = 0; j < i; j++)
{
if (arr[j] > arr[j + 1]) //只需更换一处,便能实现逆序.
{
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
------------------------------------------------------------------------------------
数组----二分法查找:
二分法查找的特点:
1.二分法查找像绕棍法,3个小棍min+max+mid来回绕.
2.min+max+mid这3者有可能重合,也有可能不重合,mid将被绕过并与之相邻.
1.二分法查找像绕棍法,3个小棍min+max+mid来回绕.
2.min+max+mid这3者有可能重合,也有可能不重合,mid将被绕过并与之相邻.
二分法查找的实现:
static int halfSearch(int[] arr, int key)
{
int min = 0, max = arr.length - 1, mid;
while (min <= max)
{
mid = (min + max) / 2;
if (key == arr[mid]) //认为就与中间的相等.
return mid;
else if (key > arr[mid])
min = mid + 1;
else
max = mid - 1;
}
return -1;
}
注意:
min+max是奇数也好,是偶数也好,都不会影响程序的判断.
------------------------------------------------------------------------------------
static int halfSearch(int[] arr, int key)
{
int min = 0, max = arr.length - 1, mid;
while (min <= max)
{
mid = (min + max) / 2;
if (key == arr[mid]) //认为就与中间的相等.
return mid;
else if (key > arr[mid])
min = mid + 1;
else
max = mid - 1;
}
return -1;
}
注意:
min+max是奇数也好,是偶数也好,都不会影响程序的判断.
------------------------------------------------------------------------------------
