Java路径-03-Java基础语法(java中路径表示方法)
Java标识符
1.1 概念
就是程序员在定义java程序时,自定义的一些名字,例如helloworld 程序里关键字class 后跟的Demo,就是我们定义的类名。类名就属于标识符的一种。 标识符除了应用在类名上,还可以用在变量、函数名、包名上。
1.2 规则
- 标识符由26个英文字符大小写(a~zA~Z)、数字(0~9)、下划线(_)和美元符号($)组成。
- 不能以数字开头,不能是关键字
- 严格区分大小写
- 标识符的可以为任意长度
1.3 案例规范
- 包名
多个单词组成时所有字母小写(例:package com.itcast)
- 类名和接口
多个单词组成时所有单词的首字母大写(例:HelloWorld)
- 变量名和函数名
多个单词组成时第一个单词首字母小写,其他单词首字母大写(例:lastAccessTime、getTime)。
- 常量名
多个单词组成时,字母全部大写,多个单词之间使用_分隔(例:INTEGER_CACHE)
注意:只是为了增加规范性、可读性而做的一种约定,标识符在定义的时候最好见名知意,提高代码阅读性。
2 Java修饰符
2.1 分类:
- 访问控制修饰符 : default, public , protected, private
- 非访问控制修饰符 : final, abstract, static, synchronized
修饰符用来定义类、方法或者变量,通常放在语句的最前端。
2.2 访问控制修饰符
Java中,可以使用访问控制符来保护对类、变量、方法和构造方法的访问。Java 支持 4 种不同的访问权限。
- default (即缺省,什么也不写): 在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。
- private : 在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类)
- public : 对所有类可见。使用对象:类、接口、变量、方法
- protected : 对同一包内的类和所有子类可见。使用对象:变量、方法。 注意:不能修饰类(外部类)。
2.2.1 默认访问修饰符-不使用任何关键字
使用默认访问修饰符声明的变量和方法,对同一个包内的类是可见的。接口里的变量都隐式声明为 public static final,而接口里的方法默认情况下访问权限为 public。
2.2.2 私有访问修饰符-private
私有访问修饰符是最严格的访问级别,所以被声明为 private 的方法、变量和构造方法只能被所属类访问,并且类和接口不能声明为 private。 声明为私有访问类型的变量只能通过类中公共的 getter 方法被外部类访问。 Private 访问修饰符的使用主要用来隐藏类的实现细节和保护类的数据。
2.2.3 公有访问修饰符-public
被声明为 public 的类、方法、构造方法和接口能够被任何其他类访问。 如果几个相互访问的 public 类分布在不同的包中,则需要导入相应 public 类所在的包。由于类的继承性,类所有的公有方法和变量都能被其子类继承。
Java 程序的 main() 方法必须设置成公有的,否则,Java 解释器将不能运行该类。
2.2.4 受保护的访问修饰符-protected
protected 需要从以下两个点来分析说明:
- 子类与基类在同一包中:被声明为 protected 的变量、方法和构造器能被同一个包中的任何其他类访问;
- 子类与基类不在同一包中:那么在子类中,子类实例可以访问其从基类继承而来的 protected 方法,而不能访问基类实例的protected方法。
protected 可以修饰数据成员,构造方法,方法成员,不能修饰类(内部类除外)。
接口及接口的成员变量和成员方法不能声明为 protected。
子类能访问 protected 修饰符声明的方法和变量,这样就能保护不相关的类使用这些方法和变量。
2.2.5 访问控制和继承
请注意以下方法继承的规则:
- 父类中声明为 public 的方法在子类中也必须为 public。
- 父类中声明为 protected 的方法在子类中要么声明为 protected,要么声明为 public,不能声明为 private。
- 父类中声明为 private 的方法,不能够被继承。
3.3 非访问修饰符
为了实现一些其他的功能,Java 也提供了许多非访问修饰符。
static 修饰符,用来修饰类方法和类变量。 final 修饰符,用来修饰类、方法和变量,final 修饰的类不能够被继承,修饰的方法不能被继承类重新定义,修饰的变量为常量,是不可修改的。 abstract 修饰符,用来创建抽象类和抽象方法。 synchronized 和 volatile 修饰符,主要用于线程的编程。
3.3.1 static 修饰符
- 静态变量: static 关键字用来声明独立于对象的静态变量,无论一个类实例化多少对象,它的静态变量只有一份拷贝。 静态变量也被称为类变量。局部变量不能被声明为 static 变量。
- 静态方法: static 关键字用来声明独立于对象的静态方法。静态方法不能使用类的非静态变量。静态方法从参数列表得到数据,然后计算这些数据。 对类变量和方法的访问可以直接使用 classname.variablename 和 classname.methodname 的方式访问。
3.3.2 final 修饰符
- final 变量
final 表示"最后的、最终的"含义,变量一旦赋值后,不能被重新赋值。被 final 修饰的实例变量必须显式指定初始值。 final 修饰符通常和 static 修饰符一起使用来创建类常量。
- final 方法
类中的 final 方法可以被子类继承,但是不能被子类修改。 声明 final 方法的主要目的是防止该方法的内容被修改。
- final 类
final 类不能被继承,没有类能够继承 final 类的任何特性。
3.3.2 abstract 修饰符
- 抽象类:
抽象类不能用来实例化对象,声明抽象类的唯一目的是为了将来对该类进行扩充。 一个类不能同时被 abstract 和 final 修饰。如果一个类包含抽象方法,那么该类一定要声明为抽象类,否则将出现编译错误。 抽象类可以包含抽象方法和非抽象方法。
- 抽象方法
抽象方法是一种没有任何实现的方法,该方法的的具体实现由子类提供。 抽象方法不能被声明成 final 和 static。 任何继承抽象类的子类必须实现父类的所有抽象方法,除非该子类也是抽象类。 如果一个类包含若干个抽象方法,那么该类必须声明为抽象类。抽象类可以不包含抽象方法。 抽象方法的声明以分号结尾,例如:public abstract sample();
3.3.3 synchronized 修饰符
synchronized 关键字声明的方法同一时间只能被一个线程访问。synchronized 修饰符可以应用于四个访问修饰符。
3.3.4 transient 修饰符
序列化的对象包含被 transient 修饰的实例变量时,java 虚拟机(JVM)跳过该特定的变量。 该修饰符包含在定义变量的语句中,用来预处理类和变量的数据类型。 transient变量不会贯穿对象的序列化和反序列化,生命周期仅存于调用者的内存中而不会写到磁盘里进行持久化。
3.3.5 volatile 修饰符
volatile 修饰的成员变量在每次被线程访问时,都强制从共享内存中重新读取该成员变量的值。而且,当成员变量发生变化时,会强制线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。 一个 volatile 对象引用可能是 null。
4 Java变量
4.1 变量的使用定义
变量用于操作系统中,实体之间的传递,把变量看作一个在内存空间中声明的存储位置,在调用变量的时候,系统会自动的调用内存中的存储位置。 在Java中,变量又称为字段,故字段在Java中又有分类。 变量命名为驼峰输入法:如testWord,textWordOne,testWordTwo...开头首单词大写,后面单词小写。
4.2 变量类别
4.2.1 成员变量定义
成员变量又称为成员字段、实例字段。成员变量位于类体(Class)的区域范围内,所以它是属于类的。在Java中创建实例字段后,成员变量也会随之在类体加载完成后进行堆栈(内存区域)的创建。
下面为创建成员变量的语法格式:
class 类体 {
数据类型 变量名; //此为创建成员变量的语法格式
}
4.2.2 局部变量定义
局部变量是在方法下的变量,位于方法块的区域内。在方法调用的时候会进行创建局部变量,然后生成相应的空间等待执行调用。
下面为创建局部变量的语法格式:
class 类体 {
数据类型 变量名; //此为创建成员变量的语法格式
public static void main(String [] args) {
数据类型 变量名; //此为创建局部变量的语法格式,位于方法中
}
}
4.3 变量使用中的分类
变量在使用的过程中也有使用分类的定义。
4.3.1 变量在方法中的使用
变量在方法标签的括号中称为“临时参数”或者“临时变量”。
实例:
class 类体 {
public void 方法名 (数据类型 临时变量名) {
//此为创建方法的临时变量
System.out.println("输出方法的临时参数:" + 临时变量名);
}
}
注: 临时变量的使用范围(生命周期)只能在方法内使用。
4.3.2 变量在类中的使用
变量在类中称为成员变量(成员字段),加上修饰符之后又是另一种定义。
4.3.2.1 static 变量名
static 变量名: static又称为静态的意思,加入成员变量后又称为“静态变量”或者“静态字段”。
如下:
class 类体 {
static 数据类型 变量名; //此为创建静态成员变量的语法格式
}
注:静态变量在创建后会变成类的变量,不在属于通过创建对象调用的变量。
下面为创建静态类中方法的静态局部字段的语法格式:
static class 类体 {
static 数据类型 变量名; //此为创建静态成员变量的语法格式
public static void main(String [] args) {
static 数据类型 变量名; //此为创建方法中的静态局部变量的语法格式,位于方法中
}
}
注:静态变量在方法中的使用,前提是整个方法也是静态的。
4.3.2.2 final 变量名
final 变量名:
final称为最终的意思,故所创建的变量是不变的、恒等的关系存在,所以添加到变量中又称为“最终字段”或“不变的变量”。
创建最终字段的语法格式::
class 类体 {
final 数据类型 变量名; //此为创建最终不变成员变量的语法格式
}
下面为创建类中方法的最终字段的语法格式;
static class 类体 {
fianl 数据类型 变量名; //此为创建静态成员变量的语法格式
public static void main(String [] args) {
final 数据类型 变量名; //此为创建方法中的**最终字段**的语法格式,位于方法中
}
}
4.3.2.3 final static 变量名
final static 变量名:
final static 变量名所创建的对象是“最终静态的意思”,故所创建的变量是静态最终不变的存在,所以添加到变量中又称为“常量”或者“最终静态字段”(在创建的时候变量名要求大写,单词之间下划线_隔开,如“TEST_Word”)。
创建常量的语法格式:
class 类体 {
final static 数据类型 变量名; //此为创建**常量**的语法格式
}
4.4 小结
变量总体来说就三种,一种是成员变量,属于类个体的变量;一种是局部变量,属于方法中的变量;一种是临时变量,属于方法中传递参数的变量。 三种变量的使用和说定义都已经给了出来,如何赋值使用请自己尝试吧,加油哟年轻人。 如果上面三种变量弄懂嘞,搞懂了,然后就把下面修饰符所修改的static,final记下来吧,并不要求会用,在你初学的时候这些还是会有所收获的,在_变量中还会涉及到他们的定义范围,适用范围,生命周期等等。
5 Java数组
5.1 数组的定义
- 数组是相同类型数据的有序集合
- 数组描述的是相同类型的若干个数据,按照一定的先后次序排列组合而成
- 其中,每一个数据称作一个数组元素,每个数组元素可以通过一个下标(编号、标记) 来访问它,下标是从 0 开始的,如果是存 10 个数组,那么下标是从 0 ~ 9 的
- 代码举例
public class ArrayDemo01 {
//变量的类型 变量的名字 = 变量的值
public static void main(String[] args) {
int[] nums = {1,2,3,4,5,6,7,8,9,10};
System.out.println(nums[0]); //1 打印下标元素为0的内容
}
}
5.2 数组的声明创建
- 首先必须声明数组变量,才能在程序中使用数组
- 语法
dataType[] arrayRefVar; //首选的方法
dataTypr arrayRefVar[]; //效果相同,但不建议
- 代码举例
- //数组类型[] 数组名字;
int[] nums;
int nums[];
- Java 语言使用 new 操作符来创建数组
- 语法
dataType[] arrayRefVar = new dataType[srraySise];
- 代码举例
//数组类型[] 数组名字 = new 数组类型[数组容量];
int[] nums = new int[10]
- 数组的元素是通过索引访问的,数组索引从 0 开始
- 获取数组长度
//数组名字.lengtharrays.length
//nums.length
- 代码总结举例
public class ArrayDemo01 {
//变量的类型 变量的名字 = 变量的值
public static void main(String[] args) {
//int[] nums = {1,2,3,4,5,6,7,8,9,10}; //一次性给数组元素赋值
//System.out.println(nums[0]); //1
//2种定义数组方法
int[] nums; //首选定义方法
int nums2[]; //第二种定义方法 C语言语法,为了让C语言程序员而兼容。
//int[] nums = new int[10]; //一步到位定义好存放数组大小
nums = new int[10]; //这里面可以存放 10 个 int 类型的数字
//依次给数组元素赋值
nums[0] = 1;
nums[1] = 2;
nums[2] = 3;
nums[3] = 4;
nums[4] = 5;
nums[5] = 6;
nums[6] = 7;
nums[7] = 8;
nums[8] = 9;
nums[9] = 10;
//计算所有元素的和
int sum = 0;
//获取数组长度:arrays.length
for (int i = 0; i < nums.length; i++) {
sum = sum + nums[i];
}
System.out.println("总和为:" + sum); //55
}
}
5.3 数组的内存
- 数组内存分析
- 画图分析数组内存
5.4 数组的三种初始化
- 静态初始化:创建的同时赋值
- 代码语法举例
int[] a = {1, 2, 3}; //正常的静态初始化
Man[] mans = {new Man(1, 1), new Man(2, 2)}; //引入的静态初始化
- 动态初始化:包含默认初始化,未赋值的元素默认为 0
- 代码语法举例
int[] a = new int[3];
a[0] = 1;
a[1] = 2;
System.out.println(a[0]); //1
System.out.println(a[1]); //2
System.out.println(a[3]); //0 未赋值则为 0 默认初始化
- 数组的默认初始化:数组是引用类型,它的元素相当于类的实例变量,因此数组一经分配空间,其中的每个元素也被按照实例变量同样的方式被隐式初始化(0)
- 代码语法举例
int[] a = new int[1];
System.out.println(a[1]); //0 未赋值则为 0
5.5 数组的四个基本特点
- 其长度是确定的。数组一旦被创建,它的大小就是不可以改变的
- 其元素必须是相同类型,不允许出现混合类型
- 数组中的元素可以是任何数据类型,包括基本类型和引用类型
- 数组变量属于引用类型,数组也可以看成是对象,数组指定每个元素相当于该对象的成员变量。数组本身就是对象,Java 中对象是在堆中的,因此数组无论保护原始类型还是其他对象类型,数组对象本身是在堆中的
5.6 数组的边界
- 下标的合法区间:[0, length-1],如果越界就会报错 ArrayIndexOutOfBoundsException (数组下标越界异常)
- 代码举例
public class ArrayDemo02 {
public static void main(String[] args) {
//静态初始化:创建的同时赋值
int[] a = {1, 2, 3, 4, 5, 6, 7, 8};
for (int i = 0; i < a.length; i++) {
System.out.println(a[i]);
//正常遍历出 a 这个数组内所有值
}
/*
for (int i = 0; i <= a.length; i++) { //将 i < a.length 改为 i <= a.length
System.out.println(a[i]);
//先报错随后正常遍历出a这个数组内所有值
//下标为 数组长度 -1,例如 a[0] = 1 而不是 a[1] = 1。当 for 循环走到 i <= a.length 时,下标为 8 (a[8]),a(8)未赋值且超过数组最大长度因此会越界报错
}
*/
}
}
- 结论
- 数组是相同数据类型 (数据类型可以为任意类型) 的有序集合
- 数组也是对象。数组元素相当于对象的成员变量
- 数组长度是确定不可改变的。越界则报错 ArrayIndexOutOfBoundsException (数组下标越界异常)
5.7 数组的使用
- 普通的 For 循环
public class ArrayDemo03 {
public static void main(String[] args) {
int[] arrays = {1, 2, 3, 4, 5};
//打印全部的数组元素
for (int i = 0; i < arrays.length; i++) {
System.out.println(arrays[i]);
}
//计算所有元素的和
int sum = 0;
for (int i = 0; i < arrays.length; i++) {
sum += arrays[i]; //sum = sum + arrays[i]
}
System.out.println("sum=" + sum);
//查找最大元素
int max = arrays[0];
for (int i = 1; i < arrays.length; i++) { //arrays[0] 已经是 max 的默认值,因此我们改成 i = 1 ,从 1 开始
if (arrays[i] > max){
max = arrays[i];
}
}
System.out.println("max=" + max);
}
}
- For-Each 循环
public class ArrayDemo04 {
public static void main(String[] args) {
int[] arrays = {1, 2, 3, 4, 5};
//JDK1.5特性 只遍历全部值,没有下标
for (int array : arrays) { //增强型 for 循环,关键字 arrays.for 自动生成
System.out.println(array);
}
}
}
- 数组作方法入参
public class ArrayDemo04 {
public static void main(String[] args) {
int[] arrays = {1, 2, 3, 4, 5};
//调用打印数组元素方法
printArray(arrays); //更好的遍历数组,在需要时可以取到下标
}
//打印数组元素
public static void printArray(int[] arrays){ //更好的遍历数组,在需要时可以取到下标
for (int i = 0; i < arrays.length; i++) {
System.out.print(arrays[i] + " ");
}
}
}
- 数组作返回值
public class ArrayDemo04 {
public static void main(String[] args) {
int[] arrays = {1, 2, 3, 4, 5};
int[] result = reverse(arrays); //调用反转数组方法
printArray(result); //更好的遍历数组,在需要时可以取到下标
}
//打印数组元素
public static void printArray(int[] arrays){ //更好的遍历数组,在需要时可以取到下标
for (int i = 0; i < arrays.length; i++) {
System.out.print(arrays[i] + " ");
}
}
//反转数组
public static int[] reverse(int[] arrays){
int[] result = new int[arrays.length]; //保证传入的数组不会超出最大长度
//反转的操作
for (int i = 0, j = result.length-1; i < arrays.length; i++, j--) { //result.length-1 是因为下标从 0 开始而 length 长度是从 1 开始,一次使用两个组合 for 循环
result[j] = arrays[i];
}
return result; //返回值
}
}
5.8 多维数组
- 多维数组可以看成是数组的数组,比如二维数组就是一个特殊的一堆数组,其每一个元素都是一个一堆数组
- 二维数组
- 语法
int a[][] = new int[2][5];
int[][] a = {{1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 10}};
public class ArrayDemo05 {
public static void main(String[] args) {
int[][] array = {{1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 10}};
//int array[][] = new int[2][5]; //相当于一个数组内嵌套了 2 层的数组
/*
//解析如下:
a =
{
{1, 2}, //2 层嵌套的数组
{3, 4},
{5, 6},
{7, 8},
{9, 10},
}
*/
//打印指定元素的值
System.out.println(array[0][0]); //1
System.out.println(array[0][1]); //2
System.out.println(array[1][1]); //4
System.out.println(array.length); //5 5列
System.out.println(array[0].length); //2 2行
}
}
解析:以上二维数组 a 可以看成一个两行五列的数组 三维数组 语法
int a[][][] = new int[][][];
int[][][] a = {{{1, 2}, {3, 4}}};
代码举例
public class ArrayDemo05 {
public static void main(String[] args) {
int[][][] array = {{{1, 2}, {3, 4}},{{5, 6}, {7, 8}}, {{9, 10}, {11, 12}}};
//int array[][][] = new int[2][2][3]; //相当于一个数组内嵌套了 3 层的数组
/*
//解析如下:
a =
{
{
{1, 2},
{3, 4},
},
{
{5, 6},
{7, 8},
},
{
{9, 10},
{11, 12},
},
//3 层嵌套的数组
}
*/
//打印指定元素的值
System.out.println(array[0][0][0]); //1
System.out.println(array[0][0][1]); //2
System.out.println(array[1][1][1]); //8
System.out.println(array.length); //3 3组
System.out.println(array[0].length); //2 2列
System.out.println(array[0][1].length); //2 2行
}
}
解析:以上三维数组 a 可以看成一个两行两列三组的数组
- 遍历多维数组
- 遍历二维数组代码举例
public class ArrayDemo05 {
public static void main(String[] args) {
int[][] array = {{1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 10}};
//遍历
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array[i].length; j++) {
System.out.println(array[i][j]);
}
}
}
}
遍历三维数组代码举例
public class ArrayDemo06 {
public static void main(String[] args) {
int[][][] array = {{{1, 2}, {3, 4}},{{5, 6}, {7, 8}}, {{9, 10}, {11, 12}}};
//遍历
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array[i].length; j++) {
for (int k = 0; k < array[i][j].length; k++) {
System.out.println(array[i][j][k]);
}
}
}
}
}
5.9 数组的 Arrays 类
- 调用数组的工具类 java.util.Arrays
import java.util.Arrays; //调用工具类
public class ArraysDemo07 {
public static void main(String[] args) {
int[] a = {1,2,3,4,3554,65,6,231324};
Arrays.sort(a);//所有的 Arrays 都需要调用 import java.util.Arrays; 工具类
}
- 由于数组对象并没有什么方法可以供我们调用,但 API 中提供了一个工具类 Arrays 供我们使用,从而可以对数据对象进行一些基础操作
- 查看 Arrays 类内所有方法
- 方法①:打开 JDK 帮助文档(搜索 Arrays)查看
- Arrays 类中的方法都是 static 修饰的静态方法,在使用的时候可以直接使用类名进行调用,而“不用”使用对象来调用(注意:是“不用”而不是“不能”)
import java.util.Arrays;
public class ArraysDemo07 {
public static void main(String[] args) {
int[] a = {1,2,3,4,3554,65,6,231324};
//直接调用
printArray(a); //[1, 2, 3, 4, 3554, 65, 6, 231324]
}
//创建方法 使用 static 修饰符
public static void printArray(int[] a){
System.out.print("[");
for (int i = 0; i < a.length; i++) {
String symbol = "";
if (i != a.length-1){
symbol = ", ";
}else{
symbol = "";
}
System.out.print(a[i] + symbol);
}
System.out.println("]");
}
}
- 它具有以下常用功能:
- 打印数组元素:通过 toString 方法直接遍历
import java.util.Arrays;
public class ArraysDemo07 {
public static void main(String[] args) {
int[] a = {1,2,3,4,3554,65,6,231324};
//打印数组元素:Arrays.tostring
System.out.println(Arrays.toString(a)); //[1, 2, 3, 4, 3554, 65, 6, 231324]
//了解原理后咱们也可以自己创建方法来实现
//可以但不建议,大家了解一下就好。不建议重复造轮子!
printArray(a); //[1, 2, 3, 4, 3554, 65, 6, 231324]
}
//创建方法
public static void printArray(int[] a){
System.out.print("[");
for (int i = 0; i < a.length; i++) {
String symbol = "";
if (i != a.length-1){
symbol = ", ";
}else{
symbol = "";
}
System.out.print(a[i] + symbol);
}
System.out.println("]");
}
}
给数组重新赋值:通过 fill 方法重新填充
import java.util.Arrays;
public class ArraysDemo07 {
public static void main(String[] args) {
int[] a = {1,2,3,4,3554,65,6,231324};
//数组填充(重新赋值)
//Arrays.fill(a, 0); //全部重新赋值为 0
//System.out.println(Arrays.toString(a)); //[0, 0, 0, 0, 0, 0, 0, 0]
Arrays.fill(a, 2, 4, 0); //包括下标为 2 但不包括下标为 4 之间的元素重新赋值为 0
System.out.println(Arrays.toString(a)); //[1, 2, 0, 0, 3554, 65, 6, 231324]
}
}
对数组排序:通过 sort 方法,按升序排序
import java.util.Arrays;
public class ArraysDemo07 {
public static void main(String[] args) {
int[] a = {1,2,3,4,3554,65,6,231324};
Arrays.sort(a);//数组进行排序:升序
System.out.println(Arrays.toString(a)); //[1, 2, 3, 4, 6, 65, 3554, 231324]
}
}
比较数组:通过 equals 方法比较数组中元素值是否相等
import java.util.Arrays;
public class ArraysDemo07 {
public static void main(String[] args) {
int[] a = {1,2,3,4,3554,65,6,231324};
//给数组中的元素值比较是否相等
int[] b = {0};
if(Arrays.equals(a, b)) { //不相等
System.out.println("相等");
}else{
System.out.println("不相等");
}
int[] c = {1,2,3,4,3554,65,6,231324};
if(Arrays.equals(a, c)) { //相等
System.out.println("相等");
}else{
System.out.println("不相等");
}
}
}
查找数组元素:通过 binarySearch 方法能对排序好的数组进行二分查找法操作
import java.util.Arrays;
public class ArraysDemo07 {
public static void main(String[] args) {
int[] a = {1,2,3,4,3554,65,6,231324};
//对数组进行二分查找法操作
Arrays.sort(a); //使用二分查找法的数组必须是有序的
System.out.println(Arrays.binarySearch(a, 0)); //2 查找元素值 3,找到后返回下标。
/*
若未找到元素值则会出现以下情况:
[1] 搜索值不是数组元素值,大小在所有数组范围内,下标从1开始计数,返回“ - 按顺序插入点的下标”
[2] 搜索值是数组元素值,下标从0开始计数,返回搜索值的下标
[3] 搜索值不是数组元素值,且大于数组内所有元素值,返回 – (数组最大长度 + 1)
[4] 搜索值不是数组元素值,且小于数组内所有元素值,返回 – 1
*/
}
}
5.10 数组的冒泡排序
- 冒泡排序无疑是最为出名的排序算法之一 (总共有八大排序)
- 冒泡排序的步骤 (两两比较)
- 比较数组中,两个相邻的元素,如果第一个数比第二个数大,我们就将它们交换位置
- 每一次比较,都会产生出一个最大或者最小的数字
- 下一轮则可以少一次排序
- 依次循环,直到结束
- 代码举例
import java.util.Arrays;
public class ArrayDemo08 {
public static void main(String[] args) {
int[] a = {2,4,2,1,3,5,6,66,77,7,4,0};
int[] sort = sort(a); //调用完我们创建的排序方法后返回一个排序后的数组
System.out.println(Arrays.toString(sort));
}
//冒泡排序方法
public static int[] sort(int[] array){
//临时变量
int temp = 0;
//外层循环:判断我们这个要走多少次
for (int i = 0; i < array.length-1; i++) { //当循环走到最后的下标时无法与下个下标进行比较就会溢出,因此这里需要 array.length-1 预留一个位置进行比较排序
//内层循环:比较判断两个数,如果第一个数比第二个数大,则交换位置
for (int j = 0; j < array.length-1-i; j++) { //每次遍历比较都要排除掉i且给最后下标的元素值预留1个位置,因此需要 array.length-1-i
if (array[j+1]
- 冒泡的代码还是相当简单的,两层循环,外层冒泡轮数,里层依次比较,江湖中人尽皆知
- 我们看到嵌套循环,应该立马就可以得出这个算法的时间复杂度为 O(n2)
- 优化代码举例
import java.util.Arrays;
public class ArrayDemo08 {
public static void main(String[] args) {
int[] a = {2,4,2,1,3,5,6,66,77,7,4,0};
int[] sort = sort(a); //调用完我们创建的排序方法后返回一个排序后的数组
System.out.println(Arrays.toString(sort));
}
//冒泡排序方法
public static int[] sort(int[] array){
//临时变量
int temp = 0;
//外层循环:判断我们这个要走多少次
for (int i = 0; i < array.length-1; i++) { //当循环走到最后的下标时无法与下个下标进行比较就会溢出,因此这里需要 array.length-1 预留一个位置进行比较排序
boolean flag = false; //通过 flag 标识减少没有意义的比较
//内层循环:比较判断两个数,如果第一个数比第二个数大,则交换位置
for (int j = 0; j < array.length-1-i; j++) { //每次遍历比较都要排除掉i且给最后下标的元素值预留1个位置,因此需要 array.length-1-i
if (array[j+1]
5.11 稀疏数组
- 稀疏数组的介绍
- 当一个数组中大部分元素为 0,或者为同一数值的数组时,可以使用稀疏数组来保存该数组
- 稀疏数组的处理方式:
- 记录数组一共有几行几列,有多少个不同值
- 把具有不同值的元素和行列以及值记录在一个小规模的数组中,从而缩小程序的规模
- 如下图 (左边是原始数组,右边是稀疏数组)
下标 0 的稀疏数组:记录整个数组有 6 行 7 列 8 个不为 0 的有效值 下标 1~8 的稀疏数组:记录每个有效值的位置。例如下标 1 中的 22 ,在数组中,下标从 0 开始计数,那么在稀疏数组中记录为 0 3 22,即为在下标为 0 的行,下标为 3 的列,记录值为 22
- 稀疏数组的应用
- 要求:编写五子棋游戏中,有存盘退出和续上盘的功能
问题分析:因为该二维数组的很多值是默认值 0,因此记录了很多没有意义的数据
解决方法:稀疏数组
代码举例
public class ArrayDemo09 {
public static void main(String[] args) {
//1.创建一个二维数组 11*11(11行11列) 0:没有棋子 1:黑棋 2:白棋
int[][] array1 = new int[11][11];
array1[1][2] = 1; //黑棋位于2行3列(数组从 0 计数)
array1[2][3] = 2; //白棋位于3行4列(数字从 0 计数)
//输出原始的数组
System.out.println("输出原始的数组");
for (int[] ints : array1) {
for (int anInt : ints) {
System.out.print(anInt + "\t");
}
System.out.println();
}
//转换为稀疏数组来保存
//1. 获取有效值的个数
int sum = 0;
for (int i = 0; i < array1.length; i++) { //array1.length 取二维数组嵌套的数组个数 11行
for (int j = 0; j < array1[i].length; j++) { //array1[0].length 取遍历下标 i 的子数组中值的个数 11列
if (array1[i][j] != 0){
sum++;
}
}
}
System.out.println("有效值的个数:" + sum);
//2. 创建一个稀疏数组
int[][] array2 = new int[sum+1][3];
//之所以行数为有效值个数加 1 是因为需要多加 1 行记录行总数、列总数、有效值个数。固定 3 列
//多出来的 1 行用用以下代码赋值记录行总数、列总数、有效值个数
array2[0][0] = 11;
array2[0][1] = 11;
array2[0][2] = sum;
//遍历二维数组,将非零的值(有效值),存放在稀疏数组中
int count = 0;
for (int i = 0; i < array1.length; i++) {
for (int j = 0; j < array1[i].length; j++) {
if (array1[i][j] != 0){
count++; //行递增
//利用行递增给稀疏数组赋值记录
array2[count][0] = i;
array2[count][1] = j;
array2[count][2] = array1[i][j];
}
}
}
//3. 输出稀疏数组
System.out.println("稀疏数组");
for (int i = 0; i < array2.length; i++) {
System.out.println(array2[i][0] + "\t" + array2[i][1] + "\t" + array2[i][2]);
}
//数组还原归位
//1. 读取稀疏数组
int[][] array3 = new int[array2[0][0]][array2[0][1]]; //根据数组默认初始化,至此一行代码已还原所有 0 值归为
//2. 给其中的元素还原它的值
for (int i = 1; i < array2.length; i++) { //还原过程中,头部记录的综合信息不需要读取,因此 i=1,从下标 1 开始
array3[array2[i][0]][array2[i][1]] = array2[i][2]; //在已记录的有效值位置还原有效值
}
//3. 打印
System.out.println("打印还原的数组");
for (int[] ints : array3) {
for (int anInt : ints) {
System.out.print(anInt + "\t");
}
System.out.println();
}
}
}
6 Java枚举
6.1 枚举概述
枚举:列举,一个一个地列出来。 Java枚举:把某个类型的对象,全部列出来。
- 什么情况下会用到枚举类型?
某些类的对象只有确定的有限个时,可以把这样的类声明为枚举。
例如:
星期:Monday(星期一)......Sunday(星期天)
性别:Male(男)、Female(女)
月份:January(1月)......December(12月)
季节:Spring(春天)......Winter(冬天)
- 枚举是一种特殊的类,特殊在它的对象是有限的几个常量对象。它既是一种类(class)类型却又比类类型多了些特殊的约束,枚举的本质是一种受限制的类。
当需要定义一组常量时,可以使用枚举。
6.2 枚举类型的定义
枚举是JDK1.5新增的引用数据类型。
- 枚举的对象的属性不应允许被改动,所以应该使用 private final 修饰
- 属性应该在构造器中为其赋值
- 若枚举显式的定义了带参数的构造器,则在列出枚举值时也必须对应地传入参数
- 枚举的实现:
- jdk1.5之前,自定义枚举类
- jdk1.5,可以使用 enum 关键字定义枚举
6.2.1 自定义枚举类
- 要求:
- 私有化类的构造器,保证不能在类的外部创建其对象
- 在类的内部创建枚举类的实例,并用声明为 public static final 的变量来引用
- 对象如果有实例变量,应该声明为private final,并在构造器中初始化
class Season {
//声明Season对象的属性:使用 private final 修饰
private final String seasonName;
private final String seasonDesc;
//私有化Season类的构造器,并给对象属性赋值
private Season(String seasonName, String seasonDesc) {
this.seasonName = seasonName;
this.seasonDesc = seasonDesc;
}
//在类的内部创建当前枚举类的对象,变量用 public static final 修饰
//public:外部可以使用枚举类的实例
//static:外部可以使用“类名.对象名”调用
//final:修饰的变量只能指向一个对象,保存的地址值不能更改
public static final Season SPRING = new Season("春天", "春暖花开");
public static final Season SUMMER = new Season("夏天", "烈日炎炎");
public static final Season AUTUMN = new Season("秋天", "秋高气爽");
public static final Season WINTER = new Season("冬天", "白雪皑皑");
//其他需求
//获取枚举类的对象的属性
public String getSeasonName() {
return seasonName;
}
public String getSeasonDesc() {
return seasonDesc;
}
//提供toString()
@Override
public String toString() {
return "Season{" +
"seasonName='" + seasonName + '\'' +
", seasonDesc='" + seasonDesc + '\'' +
'}';
}
}
public class SeasonTest {
public static void main(String[] args) {
Season spring = Season.SPRING;
System.out.println(spring.getSeasonName());
System.out.println(spring);
}
}
2.2 使用enum定义枚举
jdk1.5之前,枚举的定义格式太繁琐,存在很多重复代码 使用 enum 关键字简化枚举的定义
- 声明格式:
[修饰符] enmu 枚举名{
//实例列表
//其他成员列表
}
枚举的所有实例必须在其他成员的前面显式列出(多个实例之间 , 分隔 ; 结尾,只存在实例列表时,最后一个实例后面的 ; 可省略,存在其他成员时,不能省略),它们实际上都是 public static final 修饰的常量对象,不用自己添加修饰符,填上反而会报错。
枚举的所有构造器只能使用 private 访问控制符,并且是隐式私有的。
switch 表达式的case 子句可以直接使用 Enum定义的枚举的对象的名字。
如果枚举中只有一个对象,则可以作为单例模式的一种实现方式。
//RED、GREEN、BLUE不是枚举Color的属性,而是它的对象
enum Color {
//只有对象列表,可省略最后一个对象后的;
//对象后面没有“(参数列表)”就是调用无参构造
RED, GREEN, BLUE
}
//SPRING、SUMMER、AUTUMN、WINTER就是枚举Season的对象
enum Season {
//对象列表必须写在类的前面,多个对象之间用,隔开,末尾对象;结束
//对象后面有“(参数列表)”就是调用有参构造
SPRING("春天", "春暖花开"),
SUMMER("夏天", "烈日炎炎"),
AUTUMN("秋天", "秋高气爽"),
WINTER("冬天", "白雪皑皑");
//声明Season对象的属性:使用 private final 修饰
private final String seasonDesc;
private final String seasonName;
//私有化Season类的构造器,并给对象属性赋值
private Season(String seasonName, String seasonDesc) {
this.seasonName = seasonName;
this.seasonDesc = seasonDesc;
}
//其他需求
//获取枚举的对象的属性
public String getSeasonName() {
return seasonName;
}
public String getSeasonDesc() {
return seasonDesc;
}
//一般不用重写toString
}
public class SeasonTest {
public static void main(String[] args) {
Color blue = Color.BLUE;
System.out.println(blue); //BLUE
System.out.println(Color.class.getSuperclass()); //class java.lang.Enum
Season summer = Season.SUMMER;
System.out.println(summer); //SUMMER
System.out.println(Season.class.getSuperclass()); //class java.lang.Enum
Season s = Season.SPRING;
switch(s) {
case SPRING:
System.out.println("春暖花开");
break;
case SUMMER:
System.out.println("烈日炎炎");
break;
case AUTUMN:
System.out.println("秋高气爽");
break;
case WINTER:
System.out.println("白雪皑皑");
break;
}
}
}
6.3 枚举的隐含直接父类:Enum类
枚举不能继承其他类型,因为枚举类型有一个隐含的直接父类 java.lang.Enum :它是所有枚举的父类。
public abstract class Enum>
implements Comparable, Serializable {...}
Enum类中有一个唯一的构造器:protected Enum(String name, int ordinal)
这个构造器不是程序员手动调用的,是编译器自动调用,在所有枚举类型的构造器的首行,并且自动传入name和ordinal的值。
name:就是枚举对象名称
ordinal:就是枚举对象的序号(在枚举声明中的位置),其中初始常量序号为0
6.3.1 Enum类的主要方法
除了toString方法,都是final修饰的方法,因此都不能重写。
toString():返回此枚举常量的名称,与其在枚举声明中的声明完全相同
String name():返回此枚举常量的名称,与其在枚举声明中的声明完全相同,一般用toString()
int ordinal():返回此枚举常量的序号(在枚举声明中的位置),其中初始常量序号为0
- API中没有的方法,是编译器帮我们生成的方法:
values()方法:返回枚举类型的对象数组。该方法可以很方便地遍历所有的枚举值。
valueOf(String str):根据枚举常量的名称返回对应的枚举对象。要求字符串必须是枚举对象的“名字”。如不是,会有运行时异常
//便利枚举的对象
Season[] seasons = Season.values();
for (Season season : seasons) {
System.out.println(season);
}
//SPRING
//SUMMER
//AUTUMN
//WINTER
Season spring = Season.valueOf("SPRING");
System.out.println(spring); //SPRING
6.4 枚举实现接口
和普通 Java 类一样,枚举也可以实现一个或多个接口。
若每个枚举值在调用实现的接口方法呈现相同的行为,则只要统一实现该方法即可。
若需要每个枚举值在调用实现的接口方法呈现出不同的行为,则可以让每个枚举值分别来实现该方法。
定义接口:
interface MyInter {
void message();
}
interface Info {
void show();
}
枚举实现接口:
enum Season implements MyInter, Info {
SPRING("春天", "春暖花开") {
@Override
public void show() {
System.out.println("春天在哪里");
}
},
SUMMER("夏天", "烈日炎炎") {
@Override
public void show() {
System.out.println("夏天的风");
}
},
AUTUMN("秋天", "秋高气爽") {
@Override
public void show() {
System.out.println("秋天不回来");
}
},
WINTER("冬天", "白雪皑皑") {
@Override
public void show() {
System.out.println("大约在冬季");
}
};
private final String seasonDesc;
private final String seasonName;
private Season(String seasonName, String seasonDesc) {
this.seasonName = seasonName;
this.seasonDesc = seasonDesc;
}
//统一实现的重写方法
@Override
public void message() {
System.out.println("一年四季各不同");
}
}
测试类:
public class SeasonTest {
public static void main(String[] args) {
Season spring = Season.SPRING;
spring.message();
Season summer = Season.SUMMER;
summer.message();
Season autumn = Season.AUTUMN;
autumn.show();
Season winter = Season.WINTER;
winter.show();
}
}
一年四季各不同
一年四季各不同
秋天不回来
大约在冬季
对于同一个方法,两种重写方式可任选其一,也可同时存在。分别重写时,所有的实例都要自己重写接口中的方法,否则必须添加枚举的统一重写方法。同时存在时,实例自己的重写方法会覆盖枚举的统一重写方法。
枚举既可以包含具体方法,也可以包含抽象方法。 如果枚举含有抽象方法,则枚举的每个实例都必须分别实现该方法。与实现接口类似。
6.5 枚举的应用
6.5.1 在 switch 中使用枚举类
枚举类常应用于 switch 语句中:
enum Color
{
RED, GREEN, BLUE;
}
public class MyClass {
public static void main(String[] args) {
Color myVar = Color.BLUE;
switch(myVar) {
case RED:
System.out.println("红色");
break;
case GREEN:
System.out.println("绿色");
break;
case BLUE:
System.out.println("蓝色");
break;
}
}
}
6.5.2 实现接口,消除 if/else
我们创建的枚举类默认是被final修饰,并且默认继承了Enum类。因此不能再继承其他的类。但是可以去实现接口。
有这样一个判断场景。
if ("dog".equals(animalType)){
System.out.println("吃骨头");
} else if ("cat".equals(animalType)) {
System.out.println("吃鱼干");
} else if ("sheep") {
System.out.println("吃草");
}
怎样用枚举来消除掉 if/else 呐,看下面的代码:
先定义一个接口,里面有一个通用方法 eat()
public interface Eat {
//吃
String eat();
}
然后创建枚举类实现这个接口
public enum AnimalEnum implements Eat {
Dog(){
@Override
public void eat() {
System.out.println("吃骨头");
}
},
Cat() {
@Override
public void eat() {
System.out.println("吃鱼干");
}
},
Sheep() {
@Override
public void eat() {
System.out.println("吃草");
}
}
}
调用的时候只需要一行代码:
public class Test {
public static void main(String[] args) {
AnimalEnum.valueOf("Cat").eat(); // 吃鱼干
}
}
而且这样一来,以后假如我想扩充新的动物,只需要去枚举类中加代码即可,而不用改任何老代码,符合开闭原则!
6.5.3 枚举类中定义抽象方法
枚举类除了可以实现接口外,还可以在枚举类中定义抽象方法,这样每个枚举的对象只要分别实现了此抽象方法即可。
enum Color{
RED{
public String getColor(){//枚举对象实现抽象方法
return "红色";
}
},
GREEN{
public String getColor(){//枚举对象实现抽象方法
return "绿色";
}
},
BLUE{
public String getColor(){//枚举对象实现抽象方法
return "蓝色";
}
};
public abstract String getColor();//定义抽象方法
}
public class Test{
public static void main(String[] args) {
for (Color c:Color.values()){
System.out.print(c.getColor() + "、");
}
}
}
6.5.4 单例模式中应用
枚举在单例模式的一种实现方式中也可以用到。
/**
* @Description: 枚举 线程安全
*/
public class SingletonExample {
/**
* 构造函数私有化,避免外部创建实例
*/
private SingletonExample(){}
private static SingletonExample getInstance() {
return Singleton.INSTANCE.getInstance();
}
private enum Singleton {
INSTANCE;
private SingletonExample instance;
// JVM 保证这个方法绝对只调用一次
Singleton() {
instance = new SingletonExample();
}
public SingletonExample getInstance() {
return instance;
}
}
}
7 Java关键字
7.1 关键字
Java关键字是电脑语言里事先定义的,有特别意义的标识符,有时又叫保留字,还有特别意义的变量。Java的关键字对Java的编译器有特殊的意义,他们用来表示一种数据类型,或者表示程序的结构等,关键字不能用作变量名、方法名、类名、包名和参数。
在Java中目前一共有53个关键字:其中由51+2个保留字=53个关键字。
48个关键字:abstract、assert、boolean、break、byte、case、catch、char、class、continue、default、do、double、else、enum、extends、final、finally、float、for、if、implements、import、int、interface、instanceof、long、native、new、package、private、protected、public、return、short、static、strictfp、super、switch、synchronized、this、throw、throws、transient、try、void、volatile、while。
3个特殊直接量:true、false、null。
2个保留字:goto、const。
7.1 关键字说明
【访问控制】
private:私用模式,访问控制修饰符,可以应用于类、方法或字段(在类中声明的变量);
protected:保护模式,可以应用于类、方法或字段(在类中声明的变量)的访问控制修饰符;
public:共用模式,可以应用于类、方法或字段(在类中声明的变量)的访问控制修饰符。
【类、方法和变量修饰符】
abstract:表明类或者成员方法具有抽象属性,用于修改类或方法;
class:声明一个类,用来声明新的Java类;
extends:表明一个类型是另一个类型的子类型。对于类,可以是另一个类或者抽象类;对于接口,可以是另一个接口;
final:用来说明最终属性,表明一个类不能派生出子类,或者成员方法不能被覆盖,或者成员域的值不能被改变,用来定义常量;
implements:表明一个类实现了给定的接口;
interface:接口;
native:用来声明一个方法是由与计算机相关的语言(如C/C++/FORTRAN语言)实现的;
new:用来创建新实例对象;
static:表明具有静态属性;
strictfp:用来声明FP_strict(单精度或双精度浮点数)表达式遵循IEEE 754算术规范;
synchronized:表明一段代码需要同步执行;
transient:声明不用序列化的成员域;
volatile:表明两个或者多个变量必须同步地发生变化。
【程序控制】
break:提前跳出一个块;
continue:回到一个块的开始处;
return:从成员方法中返回数据;
do:用在do-while循环结构中;
while:用在循环结构中;
if:条件语句的引导词;
else:用在条件语句中,表明当条件不成立时的分支;
for:一种循环结构的引导词;
instanceof:用来测试一个对象是否是指定类型的实例对象;
switch:分支语句结构的引导词;
case:用在switch语句之中,表示其中的一个分支;
default:默认,例如:用在switch语句中,表明一个默认的分支Java8 中也作用于声明接口函数的默认实现。
【错误处理】
try:尝试一个可能抛出异常的程序块;
catch:用在异常处理中,用来捕捉异常;
throw:抛出一个异常;
throws:声明在当前定义的成员方法中所有需要抛出的异常。
【包相关】
import:表明要访问指定的类或包;
package:包。
【基本类型】
boolean:基本数据类型之一,声明布尔类型的关键字;
byte:基本数据类型之一,字节类型;
char:基本数据类型之一,字符类型;
double:基本数据类型之一,双精度浮点数类型;
float:基本数据类型之一,单精度浮点数类型;
int:基本数据类型之一,整数类型;
long:基本数据类型之一,长整数类型;
short:基本数据类型之一,短整数类型;
null:空,表示无值,不能将null赋给原始类型(byte、short、int、long、char、float、double、boolean)变量;
true:真,boolean变量的两个合法值中的一个;
false:假,boolean变量的两个合法值之一。
【变量引用】
super:表明当前对象的父类型的引用或者父类型的构造方法;
this:指向当前实例对象的引用,用于引用当前实例;
void:声明当前成员方法没有返回值,void可以用作方法的返回类型,以指示该方法不返回值。
【保留字】
goto:保留关键字,没有具体含义;
Const:保留关键字,没有具体含义,是一个类型修饰符,使用const声明的对象不能更新。
8 Java注释
有三种注释:单行注释,多行注释与文档注释
- 单行注释: //,最常用的注释其注释内容从 // 开始到本行结尾。
- 多行注释 从 /* 开始直至第一个 */ 出现都属于多行注释,但多行注释不能嵌套,多行注释也可以注释掉不需要的代码
- 文档注释 可以自动地生成文档,这种注释以 /** 开始,以 */ 结束
8.1 单行注释与多行注释
/**
* 这就是传说中的文档注释
* @author 作者
* 可以自动生成文档
*/
public class Hello{
public static void main(String[] args){
//这是单行注释
System.out.println("你好");
/*
System.out.println("你好");
System.out.println("你好");
这里都是多行注释
*/
}
}
8.2 文档注释
使用文档注释/** */,文档注释一般用于对类和方法进行功能说明 ,说明类的编写时间和作者以及方法作用参数和返回值
8.2.1 类上的注释
/**
* 类上的文档注释
* @author 张三 *
* @version 1.10.0
**/
8.2.2 方法上的注释
/**
* 方法前的文档注释
* @param
* @return
* @throws
**/
8.2.3 注释标签
标签 | 描述 | 示例 |
@author | 标识一个类的作者 | @author description |
@deprecated | 指名一个过期的类或成员 | @deprecated description |
{@docRoot} | 指明当前文档根目录的路径 | Directory Path |
@exception | 标志一个类抛出的异常 | @exception exception-name explanation |
{@inheritDoc} | 从直接父类继承的注释 | Inherits a comment from the immediate surperclass. |
{@link} | 插入一个到另一个主题的链接 | {@link name text} |
{@linkplain} | 插入一个到另一个主题的链接,但是该链接显示纯文本字体 | Inserts an in-line link to another topic. |
@param | 说明一个方法的参数 | @param parameter-name explanation |
@return | 说明返回值类型 | @return explanation |
@see | 指定一个到另一个主题的链接 | @see anchor |
@serial | 说明一个序列化属性 | @serial description |
@serialData | 说明通过writeObject( ) 和 writeExternal( )方法写的数据 | @serialData description |
@serialField | 说明一个ObjectStreamField组件 | @serialField name type description |
@since | 标记当引入一个特定的变化时 | @since release |
@throws | 和 @exception标签一样. | The @throws tag has the same meaning as the @exception tag. |
{@value} | 显示常量的值,该常量必须是static属性。 | Displays the value of a constant, which must be a static field. |
@version | 指定类的版本 | @version info |
9 继承
9.1 继承概述
继承是面向对象的三大特征之一,可以使得子类具有父类的属性和方法,还可以在子类中重新定义,追加属性和方法。
- 继承是指在原有类的基础上,进行功能扩展,创建新的类型。
- 继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模。
- JAVA中类只有单继承,没有多继承!
- 继承是类和类之间的一种关系。除此之外,类和类之间的关系还有依赖、组合、聚合等。
- 继承关系的两个类,一个为子类(派生类),一个为父类(基类)。子类继承父类,使用关键字extends来表示。
- 子类和父类之间,从意义上讲应该具有"is a"的关系。
- extends的意思是“扩展”,子类是父类的扩展。
继承的格式:
- 格式: public class 子类名 extends 父类名{}
- 例如: public class Zi extends Fu {}
- Fu:是父类,也被称为基类、超类
- Zi: 是子类,也被称为派生类
继承中子类的特点:
- 子类可以有父类的内容,子类还可以有自己特有的内容。
9.2 继承的优缺点
继承好处:
- 实现了数据和方法的共享
- 提高了代码的复用性(多个类相同的成员可以放到同一个类中)
- 提高了代码的维护性(如果方法的代码需要修改,修改一处即可)
- 提高了代码的可扩展性
继承弊端:
- 继承让类与类之间产生了关系,类的耦合性增强了,当父类发生变化时子类实现也不得不跟看变化,削弱了子类的独立性
9.3 继承中变量的访问特点
在子类方法中访问一个变量
- 最先在子类局部范围找,如果没有就在子类成员范围找,最后在父类成员范围找,如果都没有就报错(不考虑父亲的父亲...)。
9.4 super
super 关键字的用法和 this 关键字的用法相似
- this:代表本类对象的引用(this关键字指向调用该方法的对象一般我们是在当前类中使用this关键字所以我们常说this代表本类对象的引用)
- super:代表父类存储空间的标识(可以理解为父类对象引用)
9.5 继承中构造方法的访问特点
子类中所有的构造方法默认都会访问父类中无参的构造方法。
- 因为子类会继承父类中的数据,可能还会使用父类的数据。所以,子类初始化之前,一定要先完成父类数据的初始化
- 每一个子类构造方法的第一条语句默认都是: super()
如果父类中没有无参构造方法,只有带参构造方法,该怎么办呢?
- 通过使用super关键字去显示的调用父类的带参构造方法
- 在父类中自己提供一个无参构造方法
- 推荐: 自己给出无参构造方法
9.6 继承中成员方法的访问特点
通过子类对象访问一个方法:
- 先子类成员范围找,如果找不到就在父类成员范围找,如果都没有就报错(不考虑父亲的父亲...)
9.7 方法重写
- 方法重写概述:子类中出现了和父类中一模一样的方法声明
- 方法重写的应用:当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容
- @Override: 是一个注解可以帮助我们检查重写方法的方法声明的正确性
9.8 方法重写的注意事项
- 私有方法不能被重写(父类私有成员子类是不能继承的)
- 子类方法访问权限不能更低(public>默认>私有)
9.9 java中继承的注意事项
- Java中类只支持单继承,不支持多继承
- Java中类支持多层继承
10 接口
接口与抽象类相比,使用率是最高的,所有的设计基本是围绕接口进行的,这部分内容很重要,要彻底学明白需要很长时间,与接口相关 的两个重要设计模式:工厂设计模式、代理设计模式,是需要死记硬背的。
10.1 接口的基本概念
接口是一种特殊类,但是接口中的组成比类的简单,主要由抽象方法和全局常量组成。而接口使用interface关键字来定义。
【举例】:定义一个接口
interface A{ //定义了一个接口
public static final String MSG= "hello";
public abstract void print();
}
接口是不能直接实例化对象的,当一个接口定义完成后,按如下步骤进行接口的使用:
- 1)接口一定要定义子类,子类利用implements关键字来实现接口,一个子类可以实现多个接口;
- --秒杀抽象类的单继承局限;
- 2)接口的子类必须覆写接口中的全部抽象方法;
- 3)接口的对象利用子类对象的向上转型进行实例化操作。
【举例】:使用接口
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
X x = new X();//实例化子类对象
A a = x; //子类为父接口实例化
B b = x;
a.print();
b.fun();
}
}
interface A{ //定义了一个接口
public static final String MSG= "hello";
public abstract void print();
}
interface B{
public abstract void fun();
}
class X implements A,B{//同时实现A、B两个父接口
@Override
public void print() { //覆写接口A中的抽象方法
System.out.println("你好,接口A");
}
@Override
public void fun() {//覆写接口B中的抽象方法
System.out.println(MSG);
}
}
但是,现在有这样一种操作,也能输出hello,B和A没有什么关系,却可以转换,因为X是子类。
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
A a = new X(); //X子类为父接A口实例化
B b = (B)a;
b.fun();
}
}
【注意】:关于接口的组成描述
接口里面在定义的时候就已经明确的给出了开发要求:抽象方法和全局常量,所以,以下两种接口的定义本质上是一样的。
完整定义 | 简化定义 |
interface A{ //定义了一个接口 public static final String MSG= "hello"; public abstract void print(); } | interface A{ //定义了一个接口 String MSG= "hello"; void print(); } |
如果定义接口方法时没有使用public,本质上也不是default权限,默认就是public。为了防止开发者概念混淆,所以后续开发建议在定义接口时都写上public,可以不写abstract。
interface A{ //定义了一个接口
String MSG= "hello";
public void print();
}
现在程序中出现有类、抽象类、接口,三者之间的联系需要注意:
一个普通类若要实现接口,又要继承抽象类,一定要显extends继承抽象类,再实现接口,形式如下:
class 子类 extends 抽象类 implements 接口1,接口2,...{}
【举例】:观察子类的多继承
interface A{ //定义了一个接口
public static final String MSG= "hello";
public abstract void print();
}
abstract class B{
public abstract void fun();
}
class X extends B implements A{
@Override
public void print() {
}
@Override
public void fun() {
}
}
另外,除了以上的结构外,抽象类还可以直接实现接口:
【举例】:抽象类实现接口
interface A{ //定义了一个接口
public static final String MSG= "hello";
public abstract void print();
}
abstract class B implements A{ //此时抽象类有两个抽象方法
public abstract void fun();
}
class X extends B{
@Override
public void print() {
}
@Override
public void fun() {
}
}
抽象类可以实现接口,但是反过来,接口是不能继承抽象类的,一个接口却可以使用extends关键字继承多个父接口。
【举例】:接口多继承
interface A{ //定义了一个接口
public void printA();
}
interface B{
public void printB();
}
interface C extends A,B{ //C是A与B 的子接口
public void printC();
}
class X implements C{
@Override
public void printA() {
}
@Override
public void printB() {
}
@Override
public void printC() {
}
}
虽然接口本身只能有抽象方法 和全局常量,但是内部的结构是不受限制 的,也就是 一个接口的内部可以继续定义内部类,内部抽象类,或内部接口。如果一个内部接口上使用了static定义,这个内部接口就属于外部接口。
【举例】:使用static定义内部接口
interface A{ //定义了一个接口
static interface B{
public void print();
}
}
class X implements A.B{//注意此处使用的是.........
@Override
public void print() {
}
}
对于接口的使用,有如下几点总结:
- 接口避免了单继承局限,一个子类可以实现多个接口;
- 接口中 的权限统一为public,方法都是抽象方法,大多数情况下接口中都不会定义全局常量;
- 所有的内部类结构都不受定义语法的限制,static定义的内部接口就是外部接口。
实际开发中,接口的三个使用原则:
- 制定操作的标准;
- 表示一种能力;
- 将服务器端的远程方法视图提供给客户端。
10.2 接口的应用
- 标准(连接不同的两种类)
- 工厂设计模式
- 代理设计模式。
- ……
11 Java源码
略