欢迎来到我的博客

java基础学习

目录:

一、Java 概述

  1. Java体系
  2. 常用的DOS命令
  3. JDK、JRE、跨平台、Java平台
  4. Java代码的编写执行过程
  5. 注释
  6. 关键字、标识符
  7. Java中的数据类型
  8. Java中的常量

二、变量和运算符

  1. 变量和计算机存储单元
  2. 数据类型和常量
  3. 数据类型转换
  4. 运算符 和 优先级

三、引用数据类型、流程控制语句 和 数组

  1. 引用数据类型
  2. 流程控制语句
  3. 数组

四、方法

  1. 引入
  2. 语法格式
  3. 方法调用
  4. 注意事项
  5. 方法重载
  6. 可变参数列表

五,面向对象三大特征

1, 封装
2, 继承
3,多态
4, 抽象类

六 ,接口

1, 接口的概念
2,接口的声明
3, 接口的实现
4,接口中成员变量的特点
5, 接口中成员方法的特点
6, 类和接口的多实现
7, 接口的继承
8,接口与类的区别:
9, 抽象类和接口的区别
10, 接口中的方法是否可以被重载和覆盖?
11, 接口的新特性——默认方法和静态方法(jdk8之后)
12, 标记接口

一、Java 概述

1. Java体系

java是一个体系,包含:
java语言
运行在各种平台上的虚拟机
class字节码文件格式
java api(jdk中提供的api),类库
商业的或者三方优秀开源类库

  • 编写源文件

文件扩展名是.java

  • 编译Java源程序

使用Java编译器(javac.exe)编译源文件,得到字节码文件。

  • 运行Java程序

使用JavaSE平台中的java解释器(java.exe)来解释执行字节码文件。

使用Linux(windons)环境编译执行Java程序

2. 常用的DOS命令

3. JDK、JRE、跨平台、Java平台

JDK:它是Java开发运行环境,在程序员的电脑上当然要安装JDK;
JDK = JRE + 开发工具集(例如Javac编译工具等)
JRE:Java Runtime Environment它是Java运行环境,如果你不需要开发只需要运行Java程序,那么你可以安装JRE
JRE = JVM + JavaSE标准类库
JDK 包含 JRE 包含 JVM
跨平台特性
平台指的是操作系统 (Windows,Linux,Mac)。
Java程序可以在任意操作系统上运行,一次编写到处运行
实现跨平台需要依赖Java的虚拟机 JVM (Java Virtual Machine)
为什么可以跨平台?因为JAVA程序运行依赖虚拟机,而针对不同操作系统有不同版本的虚拟机
Java语言是跨平台的,Java虚拟机不是跨平台的
Java平台
Java SE (桌面程序)标准版
Java EE (Web程序)企业版
Java ME(移动设备)微型版 -- Android,IOS兴起后就很少使用了

4. Java代码的编写执行过程

源文件:编写Java源文件(我们也称之为源代码文件),它的扩展名为.java;
编译:然后通过编译器把源文件编译成字节码文件,字节码文件扩展名为.class;
为什么要编译?JAVA程序是由虚拟机运行的,我们所写的代码虚拟机不认识,我们要把自己写的代码翻译成虚拟机所认识的语言
运行:最后使用解释器来运行字节码文件。

5. 注释

定义:用来解释和说明程序的文字,注释是不会被执行的
分类:
单行注释: //注释内容
多行注释: /注释内容/
文档注释: /**注释内容*/
** 注意:
对于单行和多行注释,被注释的文字,不会被JVM解释执行
对于文档注释,可以被JDK提供的工具 javadoc 所解析,生成一套以网页文件形式体现的该程序的说明文档
单行注释可以嵌套使用,多行注释不能嵌套使用

6. 关键字、标识符

    • 关键字
      定义:是被Java语言赋予特殊含义,具有专门用途的单词,比如之前接触的class,int,double均为Java已经预设好的
      特点:字母全部小写, 注意String不是关键字
      注意事项:goto 与 const 是Java中的保留字,即没有赋予特殊含义却仍被Java占用的单词
      标识符
      定义:就是给类,接口,方法,变量等起名字时使用的字符序列
      组成规则(只能包含下面的内容,不能有其它内容):
      英文大小写字母
      数字字符
      $和_
    • 注意事项:
      数字不能开头
      不可以使用关键字
      严格区分大小写,不限制长度
      起名时,尽量达到见名知意
      标识符中常见的命名规则(这些规定是不受语法约束的):
      包名:多单词组成时所有字母均小写,使用.连接,域名反写 aaa.bbb.ccc
      类名&接口名:大驼峰式 Aaa AaaBbbCcc
      变量名&方法名:小驼峰式 aaa aaaBbbCcc
      常量名:多单词组成是所有字母均大写,使用_连接 AAA_BBB_CCC

7. Java中的数据类型

  • 为什么有数据类型?
    Java语言是强类型语言,对于每一种数据都定义了明确的具体数据类型
    Java中数据类型的分类
    基本数据类型: 基本数据类型是Java语言中内置的类型,分别是整数类型、小数类型、字符类型、布尔类型。这四类基本类型是最简单、最基础的类型。
    引用数据类型: 是强大的数据类型,它是基于基本数据类型创建的。
    JavaSE中提供了一个超级类库,类库中包含了近万种引用数据类型。
    基本类型:类 接口 数组 枚举

8. Java中的常量

定义:常量就是不变的数据量, 在程序执行的过程中其值不可以发生改变
整形常量默认是 int类型
在Java中,定义长整形数据如果值超过int取值范围后面要+L,否则是错误的
小数常量默认是 double类型
D后缀为double,F后缀为float
定义float类型的数据后面要 + f ,否则默认是double

  • 常量分类:
    整数类型
    十进制表示方式:正常数字。 如 13、25等
    二进制表示方式:以 0b(0B) 开头。 如0b1011 、0B1001
    十六进制表示方式:以 0x(0X) 开头。 数字以0-9及A-F组成 如0x23A2、0xa、0x10
    八进制表示方式:以 0 开头。 如01、07、0721
    小数类型:如1.0、-3.15、3.168等
    布尔类型:true、false ,注意严格区分大小写
  • 字符类型:
    如 'a','A', '0', '家'
    字符必须使用 '' 包裹,并且其中只能且仅能包含一个字符
  • 字符串类型:
    字符串String类型是一种引用类型,我们先了解作为常量类型的使用方式
    如 "我爱Java","0123","","null"
    字符串必须使用 "" 包裹,任意长度
    在计算机内,有符号数有3种表示法:原码、反码和补码。所有数据的运算都是采用 补码 进行的
    原码: +8: 0 000 1000 -8: 1 000 1000
    就是二进制定点表示法,即最高位为符号位,0表示正,1表示负,其余位表示数值的大小。
    反码: +8: 0 000 1000 -8: 1111 0111
    正数的反码与其原码相同;负数的反码是对其原码逐位取反,但符号位除外。
    补码: +8: 0 000 1000 -8: 1111 1000
    正数的补码与其原码相同;负数的补码是在其反码的末位加 1

二、变量和运算符

1. 变量和计算机存储单元

变量是内存中装载数据的小盒子,你只能用它来存数据和取数据
变量名是标识符,这说明只要是合法的标识符都可以用来做变量名。
变量使用的注意事项
变量定义后可以不赋值,使用时再赋值。不赋值不能使用
变量使用时有作用域的限制。(局部变量和全局变量)
变量不可以重复定义
计算机存储单元
计算机中储存和运算的最小单位:一个字节,也就是1个字节(byte)
常用储存单位1B(字节) = 8bit(比特位)
1KB = 1024B
1MB = 1024KB
1GB = 1024MB
1TB = 1024GB
1PB = 1024TB

2. 数据类型和常量

  • 四类八种
    byte: 1个字节 : -128~127 (数据表示范围)
    short: 2个字节: -32768~32767
    int: 4个字节: -2147483648~2147483648
    long: 8个字节: -263~263-1
    float: 4个字节: -3.403E38~3.403E38
    double: 8个字节: -1.798E308~1.798E308
    char: 2个字节: 表示一个字符,如('a','A','0','家')
    boolean: 1个字节: 只有两个值true与false
    变量必须要有明确的类型,什么类型的变量装载什么类型的数据
    数据类型范围从小到大排序:
    byte < char < short < int < long < float < double,
    布尔类型boolean不能参与类型转换

3. 数据类型转换

自动类型转换:表示范围小的数据类型转换成范围大的数据类型,这种方式称为自动类型转换
自动类型转换格式:范围大的数据类型 变量 = 范围小的数据类型值;
默认转换:byte、short、char —> int —> long —> float —> double
byte、short、char 相互之间不转换,他们参与运算首先转换为 int类型
强制类型转换:表示范围大的数据类型转换成范围小的数据类型,这种方式称为强制类型转换
强制类型转换格式:范围小的数据类型 变量 = (范围小的数据类型) 范围大的数据类型值;

4. 运算符 和 优先级

  • 算数运算符
      • / % ++ --

+: 正号、加、连接字符串
++,--运算符后置时,先使用变量a原有值参与运算操作,运算操作完成后,变量a的值自增1或者自减1;
++,--运算符前置时,先将变量a的值自增1或者自减1,然后使用更新后的新值参与运算操作。

    • 注意事项:
      加法运算符在连接字符串时要注意,只有直接与字符串相加才会转成字符串。
      除法当两边为整数时,取整数部分,舍余数。当其中一边为浮点型时,按正常规则相除。
      % 为整除取余符号,小数取余没有意义。结果符号与被取余符号相同。
      整数做被除数,0不能做除数,否则报错。
      小数做被除数,整除 0 结果为 Infinity,对 0 取模结果为 NaN
  • 赋值运算符

+= -= *= /= %=

+=, -=, *=, /=这样的赋值运算符包含了一个 强制转换 的操作,会将左右两边运算后的结果,强制类型转换后赋值给左边
** 注意:赋值运算符左边必须是变量
int n = 10;
byte by = 20;
by += n; // 运算完毕后,by的值为byte类型30, 相当于代码 by = (byte)(by + n);

  • 比较运算符

== != < > <= >=

结果只能是true 和 false
字符间的比较,比较的是其 ASCII 值
浮点数 与 整数 比较,只要值相等,就返回 true

  • 逻辑运算符

&与-----false&true-----False
|或-----false|true-----True
异或-----trueflase-----True
!非-----!true-----Flase
&&短路与-----false&&true-----False
||短路或-----false||true-----True
&& : 又叫短路运算符,A&&B,如果A为假,不会去计算B的值,直接得到结果为 false
& : A & B,即使A为假,也会计算B的值。
|| : 也是短路运算符,A || B,如果A为真,不会去计算B的值,直接得到结果为 true
| : A | B,即使A为真,也会计算 B 的值。
异或^ : 左右两边条件结果相同,结果就为false,左右两边条件结果不同,结果就为true;

  • 三目运算符

语法:布尔表达式 ? 表达式1 : 表达式2
当布尔表达式的值为true,则返回表达式1的值,否则返回表达式2的值
运算符优先级

  • 优先级 描述 运算符
    1 括号 ()、[]
    2 正负号 +、-
    3 自增自减,非 ++、--、!
    4 乘除,取余 、/、%
    5 加减 +、-
    6 移位运算 <<、>>、>>>
    7 大小关系 >、>=、<、<=
    8 相等关系 ==、!=
    9 按位与 &
    10 按位异或 ^
    11 按位或 |
    12 逻辑与 &&
    13 逻辑或 ||
    14 条件运算 ?:
    15 赋值运算 =、+=、-=、
    =、/=、%=
    16 位赋值运算 &=、|=、<<=、>>=、>>>=

  • 位运算符

位运算符
位运算是直接对 二进制 进行运算
在位运算中,操作数必须是 整型
位异或运算符的特点:
一个数据对另一个数据位异或两次,该数本身不变。
任何数和自身异或,结果为0
任何数和0异或,结果为本身
<< 左移一位,相当于乘以2: 3 << 2 = 12 --> 322=12

右移一位,相当于除以2: 3 >> 1 = 1 --> 3/2=1
3 >>> 1 = 1 --> 3/2=1
同样的运算,位运算的效率高于算术运算

三、引用数据类型、流程控制语句 和 数组

1. 引用数据类型

引用数据类型的变量定义及赋值格式:数据类型 变量名 = new 数据类型();
调用该类型实例的功能:变量名.方法名();

  • Scanner类:
    导包:import java.util.Scanner;
    创建对象实例:Scanner sc = new Scanner(System.in);

  • 调用方法:
    int i = sc.nextInt(); //用来接收控制台录入的数字
    String s = sc.next(); //用来接收控制台录入的字符串
    随机数类Random

  • 方法简介
    public int nextInt(int maxValue) //产生 [0,maxValue) 范围的随机整数,包含0,不包含maxValue;
    public double nextDouble() //产生 [0,1) 范围的随机小数,包含0.0,不包含1.0。

  • Random使用方式:

  • import导包:import java.util.Random
    创建实例格式:Random 变量名 = new Random();
    赋值:a = 变量名.nextInt(maxValue);

2. 流程控制语句

  1. if
  2. if...else...
  3. if...else if...else...
  4. while
  5. for
  6. do...while...
  7. switch case default break
  • case 穿透性:如果多个case条件后面的执行语句是一样的,则该执行语句只需书写一次即可,这是一种简写的方式
  • break语句
    作用:跳出所在的循环体
    使用方式
    无法单独使用,必须将break关键字置于 switch 或 循环语句 中
    运行规律
    不需要判断任何条件,只要遇到break变直接跳出执行后续代码。会完全跳出选择或者循环结构
    只能跳出 最近的 代码块,不能跨越多级代码块
    标记
    当break语句出现在嵌套循环中的内层循环时,它只能跳出内层循环,如果想使用break语句跳出外层循环则需要对外层循环添加标记
    使用方式:在外层循环外的某行前边使用,后边跟有冒号:的标识符,即定义完毕。
    使用时当在内层循环使用 break 或 continue 时后边紧跟之前定义的标号即可
    for (j = 1; j <= i; j++) { // 内层循环
        if (i > 4) { // 判断i的值是否大于4
        break itcast; // 跳出外层循环
    }
}
  • continue语句
    作用:提前结束本次循环,继续进行下次循环
    使用方式: 无法单独使用,必须将continue关键字置于 循环语句 中
    运行规律: 不需要判断任何条件,只要遇到continue变直接跳出本轮循环进行下次循环
    return 、break、continue 跳出控制语句的区别
    break:结束当前整个循环,执行当前循环下边的语句。
    continue:结束本次循环,继续进行下次循环
    return: 用于结束一个方法,返回给上层调用者,如果位于main方法内,相当于结束了程序。
    return也可以用于结束循环,因为return用于结束一个方法,如果方法中有循环的话,不管return嵌套于多少层循环之内,循环都结束。
    不会再执行循环后面的语句

3. 数组

  • 一维数组
    概述:长度不可变、元素类型相同
    一组数据的集合,数组中的每个数据被称作'元素'
    在数组中可以存放'任意类型'的元素
    但'同一个数组'里存放的元素类型必须一致。
    数组是'恒定, 定长的
  • 数组的定义1
    格式: 数据类型[] 数组名 = new 数据类型[长度]; int[] x = new int[100];
    使用属性:数组名.length
    数组的最小索引是 0, 最大索引是 数组.length-1
  • 数组的定义2
    数组初始化:在定义数组时只指定数组的长度,由系统自动为元素 赋初值 的方式称作动态初始化。
  • 格式:
    数据类型[] 数组名 = new 类型[长度]; int[] arr = new int[4];
    类型[] 数组名 = new 类型[]{元素,元素,……}; int[] arr = new int[]{1,2,3,4};
    类型[] 数组名 = {元素,元素,元素,……}; int[] arr = { 1, 2, 3, 4 };
    JVM内存划分
  • 寄存器:内存和CUP之间
    本地方法栈: JVM调用了系统中的功能
    方法和数据共享: 运行时期class文件进入的地方
    方法栈:所有的方法运行的时候进入内存
    堆:存储的是容器和对象
    数组中常见的异常
    数组的索引越界异常
    空指针异常
  • 二维数组
    定义格式
  • 第一种定义格式
    相当于定义了一个3*4的二维数组,即二维数组的长度为3,二维数组中的每个元素又是一个长度为4的数组
  • int[][] arr = new int[3][4];//不推荐
  • 第二种定义格式
    第二种方式和第一种类似,只是数组中每个元素的长度不确定,必须要new:arr[0] = new int[5];...
    int[][] arr = new int[3][];

  • 第三种定义格式
    二维数组中定义了三个元素,这三个元素都是数组,分别为{1,2}、{3,4,5,6}、{7,8,9}
    int[][] arr = {{1,2},{3,4,5,6},{7,8,9}};
    二维数组内存

    • 比如:int[][] arr = new int[3][2];
      外层数组长在内存开辟连续的3个大的内存空间,每一个内存空间都对应的有地址值
      每一个大内存空间里又开辟连续的两个小的内存空间.
      打印数组
      打印一维数组五种方法
Arrays.toString(arr)

for(int n: arr)   
   System.out.println(n+", ");  

for (int i = 0; i < arr.length; i++) {  
   System.out.print(arr[i] + ", ");  
}  

System.out.println(Arrays.asList(arr));  

Arrays.asList(arr).stream().forEach(s -> System.out.println(s));//java8  

//打印二维数组三种
for(int i=0;i<arr.length;i++) {  
    for(int j=0;j<arr[i].length;j++) {  
        System.out.print(arr[i][j]+" ");  
    }  
    System.out.println();  //换行  
}  

for(int[] a:arr) {  
    for(int b:a){  
        System.out.print(b+" ");  
    }  
    System.out.println();//换行  
} 

for(int i=0;i<arr.length;i++)  
    System.out.println(Arrays.toString(arr[i]));  

//打印三维数组三种``
int [][][] arrays=new int[4][3][2];//在栈空间创建一个空间  

for(int i=0;i<arrays.length;i++) {  
    for(int i1=0;i1<arrays[i].length;i1++) {  
        for(int i2=0;i2<arrays[i][i1].length;i2++) {  
        System.out.print(arrays[i][i1][i2]);  
    }  
    System.out.println();//二维换行  
}  
System.out.println();//三维换行  
}  

四、方法

1. 引入

为什么要有方法:提高代码的复用性
什么是方法:完成特定功能的代码块

2. 语法格式

访问修饰符:方法允许被访问的权限范围。( public,protected,private,无修饰符 )
返回类型:void、任何数据类型
方法名:同变量名规则。小写;多个单词,第一个单词首字母小写,其余首字母大写 。myMethod
参数列表:可以省略。参数类型 + 参数名,...

3. 方法调用

可以使用 对象名 调用方法,静态方法是使用 类名 调用的
方法包括在类中,调用时先创建包含方法的类的对象,然后用对象再去调用方法。
创建对象:类名 对象名 = new 类名();
调用:对象名.方法名();

4. 注意事项

方法参数是 基本类型 ,传递的是值。(包含String类型),形式参数的改变对实际参数不影响
方法参数是 引用类型,传递的是内存地址值。(String类型除外),形式参数的改变对实际参数有影响
方法的定义只能放在类里面,不能嵌套定义。故而不能在主方法内直接定义方法
方法返回值是void,方法中可以省略return
方法一般在主方法的前面定义
调用方法的时候,返回值是void, 不能写在输出语句中

5. 方法重载

方法的重载:在同一个类中,方法名相同,参数列表不同。与返回值类型无关。
参数列表不同:
参数个数不同
参数类型不同
参数的顺序不同(算重载,但是在开发中不用),注意:必须是不同数据类型。相同类型不存在顺序不同
方法重载注意事项
重载和参数变量名无关
重载和返回值类型无关如void method(int x)与int method(int y)不是方法重载,不能同时存在
重载和修饰符无关
重载看 方法名 和 参数列表

6. 可变参数列表

例:
public void sum(int... n){}
参数列表中如果有两个以上的参数,可变参数一定在最后
可以将数组传递给可变参数列表
数组作为参数时,不能将多个值传递给数组的
一个方法的形式参数列表,只能有一个可变参数列表
方法名相同,一个参数是可变参数,一个参数是一维数组,这两个方法不是重载,因为 一个可变参数等价于相应类型的一维数组
就可以对可变参数列表,进行相应的数组操作,比如求长度
可变参数列表所在的方法,是最后被执行访问的
方法重载的时候,既可以定义有可变参数的同名方法,也可以定义有确定参数个数的方法,jvm调用方法时,会优先调用 有确定参数个数 的方法

一、封装

1. 概述

  • 定义:将类的某些信息隐藏在类的内部,不允许外部程序直接访问。只能通过该类提供的 特定的方法 来实现对隐藏信息的操作和访问,也就是:
    要隐藏对象的信息
    同时也要留出访问的接口

2. 封装的特点

隐藏类的实现细节,实现了信息的隐藏及安全性,方便修改和实现
提高了程序的模块化,提高系统独立性和软件的可重用性,且易于维护
具体实现是编写该类的人控制的,让使用者只能通过事先定制好的 方法 来访问数据,实现者可以方便地加入控制逻辑,限制对属性的不合理操作

3. 封装的实现

  • 变量:使用 private 修饰,这就是变量的封装
  • 方法:也是一种封装,封装了多条代码
  • 类: 也是一种封装,封装了多个方法
    封装的实现步骤:

封装的实现

public class Cat {
    //成员属性:
    //修改属性可见性---private 限定只能在当前类内访问,只能修饰成员变量
    private String name;
    public Cat() {
    }
    //创建get/set方法
    //在get/set方法当中添加属性的限定
    public void setName(String name) {  //set方法一般没有返回值
        this.name = name;
    }
    public String getName() { 
        return "我是一只名叫"+this.name+"的猫咪";
    }
    public int getMonth() {
        return month;
    }
    public void setMonth(int month) {   //对年龄进行限定
        if(month<=0)
            System.out.println("输入信息错误,宠物猫的年龄必须大于0");
        else
            this.month = month;
    }
}

二、 继承

1. 概述

一种类于类之间的关系,使用已存在的类作为基础建立新类。
使用已存在的类的定义作为基础创建新类
新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但 不能选择性地继承父类,必须继承父类 所有开放的特征
使用 extends 关键字实现

2. 继承的逻辑关系

不能为了继承某个功能而随意进行继承操作,必须要符合 “A is a B” 关系

3. 继承的特点:

子类会自动拥有父类所有 非 private 修饰的属性和方法
通过 子类对象 既可以调用自身的 非 private 修饰的成员,也可以调用父类的 非 private 修饰的成员
父类 不可以 访问子类 特有成员,即使是共有的
继承的出现提高了代码的复用性,提高软件开发效率。
继承的出现让类与类之间产生了关系,提供了多态的前提

4. 继承的注意事项:

在Java中,类只支持单继承,不允许多继承,一个类只能有一个直接父类
多个类可以继承一个父类:

class B extends A{}
class C extends A{}   // 类B和类C都可以继承类A

在Java中,多层继承 是可以的,即一个类的父类可以再去继承另外的父类

class A{}
class B extends A{}   // 类B继承类A,类B是类A的子类
class C extends B{}   // 类C继承类B,类C是类B的子类,同时也是类A的子类

在Java中,子类和父类是一种相对概念,一个类是某个类父类的同时,也可以是另一个类的子类

5. 继承后子类父类成员变量的特点

子类的对象调用成员变量的时候,子类自己有,使用子类,子类自己没有则调用父类
子父类中出现了 同名 的成员变量时,在子类中需要访问父类中非私有成员变量时,需要使用 super 关键字

6. 继承后子类父类成员方法的特性

子类的对象调用方法的时候,子类自己有,使用子类,子类自己没有调用的父类
子类中出现与父类一模一样的方法时,会出现覆盖操作,也称为 override 重写、复写或者覆盖

7. 继承的初始化顺序

父类中有:静态代码块,构造代码块,无参构造方法,静态属性。
子类中有:静态属性,静态代码块,构造代码块,无参构造方法

  • 执行顺序:
  1. 父类的静态代码块
  2. 子类的静态代码块
  3. 父类的构造代码块
  4. 父类的无参构造方法
  5. 子类的构造代码块
  6. 子类的无参构造方法

8. 方法重载和方法覆盖

  • 方法重载:
  1. 同一个类中
  2. 方法名:重载的两个方法的方法名必须相同
  3. 参数列表: 不同,三者至少满足一个(参数顺序、个数、类型),三者至少满足一个
  4. 重载与返回值类型、权限修饰符 无关
  5. 与方法的参数名 无关
  • 方法覆盖
  1. 有继承关系的 子类 中, 子类覆盖父类的方法
  2. 方法名:子类方法和父类方法 必须相同,
  3. 参数列表:子类方法和父类方法的形参列表 必须相同
  4. 访问修饰符:子类方法的权限 >= 父类的方法的权限
  5. 返回值类型:
  • 基本数据类型: 必须相同
  • 引用数据类型:相同 或者 子类方法的返回值类型是父类方法的返回值类型的 子类
  • 注意:
    当子类重写父类方法后,子类对象调用的是 覆盖后 的方法。
    属性名也是一样的道理,没有重名之前,调用的是父类的,重名之后用的是子类的
    被 final 修饰的方法不允许在子类中覆盖
    父类被覆盖的方法的参数列表中被声明为 final 的参数,在子类的覆盖方法的中可以不必指定为 final
    子类覆盖方法声明的 异常列表中的异常类 必须与父类被覆盖方法声明的异常列表中的异常类 兼容
    只有在 子类类体中可以访问 的父类或祖先类的方法才能被覆盖
    静态方法 不能被覆盖,只能被隐藏
    如果通过子类对象访问父类方法,在 父类类体 中,访问到的任然是子类中的覆盖方法

三、 多态

1. 概述

  • 定义:多种形态,是面向对象语言最核心特征,封装和继承都是为多态准备的,非常重要
    Java中多态的 代码体现 在一个子类对象(实现类对象)既可以给这个子类(实现类对象)引用变量赋值,又可以给这个子类(实现类对象)的父类(接口)变量赋值。
    最终多态体现为 父类引用变量可以指向子类对象
    如 Student 类可以为 Person 类的子类。那么一个S tudent 对象既可以赋值给一个 Student 类型的引用,也可以赋值给一个 Person 类型的引用。
  • 多态的分类:
    Java中的多态一般是运行时多态
  • 编译时多态:设计时多态,方法重载来实现
  • 运行时多态:程序运行时动态决定调用哪个方法
  • 多态的前提:必须有 子父类关系 或者类 实现接口关系,否则无法完成多态。
  • 多态的优点:
    提高代码的可维护行
    提高代码的扩展性
    多态的弊端:不能使用子类的特有功能
    如何解决?
    法1:创建子类对象调用子类方法
    法2:把父类的引用强转为子类引用

2. 多态调用的三种格式

父类的引用变量指向子类对象:父类类型 变量名 = new 子类类型();
普通类多态定义的格式:父类 变量名 = new 子类();
抽象类多态定义格式:抽象类 变量名 = new 抽象类子类();
接口多态定义的格式:接口 变量名 = new 接口实现类();

3. 注意事项:

同一个父类的方法会被不同的子类重写。在调用方法时,调用的为各个 子类覆盖后的方法

Person p1 = new Student();
Person p2 = new Teacher();
p1.work(); //p1会调用Student类中重写的work方法
p2.work(); //p2会调用Teacher类中重写的work方法

当变量名指向不同的子类对象时,由于每个子类覆盖父类方法的内容不同,所以会调用不同的方法。

4. 多态中成员访问的特点

成员变量
编译看左边(引用变量的声明类型),运行看左边(实际访问到的成员变量的值,也是由引用变量的声明类型来决定)
方法
编译看左边(引用变量的声明类型),运行看右边(实际访问到的方法,是由引用变量所指向的对象的实际类型来决定)

5. 编译时多态(方法重载 overload)

因为对于方法重载而言,虽然多个方法的方法名相同,但是我们的编译器,可以根据方法调用代码推断出,所要调用的那个方法的方法签名,从而根据方法签名(jvm唯一的),确定要调用的方法

  • 注:方法签名: 方法名+方法参数列表**

6. 运行时多态

因为在编译器编译的时候,无法知道,具体调用的是哪个方法的代码,只有当 jvm 具体真正执行到调用代码的地方,jvm才能知道调用的究竟是哪个方法
实现运行时多态:继承、方法覆盖/重写(override)、父类引用指向子类对象

7. 多态的转型

  • 向上转型:当有 子类对象赋值给一个父类引用 时,便是向上转型,多态本身就是向上转型的过程。(也叫:隐式转型、自动转型)
    格式:父类类型 变量名 = new 子类类型();
    向下转型:一个 已经向上转型 的子类对象可以使用 强制类型转换 的格式,将父类引用转为子类引用,这个过程是向下转型。如果是直接创建父类对象,是无法向下转型的!
  • 格式:子类类型 变量名 = (子类类型) 父类类型的变量;
    什么时候使用向上转型?
    当不需要面对子类类型时,通过提高扩展性,或者使用父类的功能就能完成相应的操作,这时就可以使用向上转型。
    什么时候使用向下转型?
    当要使用子类特有功能时,就需要使用向下转型。
  • 向下转型的好处:可以使用子类特有功能。
    但是弊端是需要面对具体的子类对象;在向下转型时容易发生 ClassCastException 类型转换异常。在转换之前必须做 类型判断
    如:
    if( !a instanceof Dog){…}

8. instanceof 关键字

  • 作用: 可以通过 instanceof 关键字来判断是否能够对象转化。也就是,一个引用型的变量,是不是这个类型的对象,提高向下转型的安全性
  • 格式: boolean b = 对象 instanceof 数据类型; ,返回 true / false
  • 注意:null instanceof <类名> 结果永远是 false
    Person p1 = new Student(); // 前提条件,学生类已经继承了人类
    boolean flag = p1 instanceof Student; //flag结果为true
    boolean flag2 = p2 instanceof Teacher; //flag结果为false

四、 抽象类

1. 概述

在Java中,一个没有方法体的方法应该定义为抽象方法,而如果一个类中含有抽象方法,则该类必须定义为一个抽象类
抽象类通常作为一个 框架(虽然在父类抽象类中,有些行为并不能具体确定,但是从类设计的角度将,我们能确定该类存在这样的行为),把子类将实现的抽象方法组织起来,简化或限制子类的设计

2. 抽象类的定义格式

  • 抽象方法定义的格式:
    public abstract 返回值类型 方法名(参数);
  • 抽象类定义的格式:
    abstract class 类名 {}

3. 抽象类特点

抽象类和抽象方法都需要被 abstract 修饰。抽象方法一定要定义在抽象类中。
static、final、private 不能与 abstract 同时出现。
抽象方法 不能有 方法体
抽象类 不一定 有抽象方法,但是含有抽象方法的类 必须是 抽象类
构造方法,类方法(用 static 修饰的方法),不能声明为抽象方法。
抽象类本身不能实例化(但是多态机制可以用子类实例化),不可以直接创建对象
原因:调用抽象方法没有意义
只有覆盖了抽象类中 所有的 抽象方法后,其子类才可以创建对象。否则该子类还是一个抽象类。
抽象类只定义了类的部分行为(包含具体行为), 这些行为是 子类共有的,其它行为由子类实现的抽象方法提供

4. 抽象类成员特点

抽象类的 成员变量:既可以变量,又可以是常量
抽象类的 构造方法:用于父类数据的初始化
子类继承抽象类时,构造方法不会被覆盖。 而且,在实例化子类对象时首先调用的是抽象类中的构造方法再调用子类中的。
因此,在抽象类中可以使用构造方法封装,所继承子类公共的东西。
抽象类的 方法:可以是抽象的,也可以是非抽象的,

  • 作用:
    抽象的:强制子类实现 (语法上的强制约束力)
    非抽象的:子类可以复用

5.抽象类的子类(二选一)

如果不想重写抽象类里面的抽象方法,则子类也必须是抽象类
如果不是抽象类,则必须实现抽象父类的所有抽象方法

6. 抽象类的细节

抽象类一定是个父类?
严格来说是的,不是父类的话就没有意义了,抽象类的抽象方法必须由子类来实现 本身只起到定义的作用
抽象类中是否可以没有抽象方法?如果可以,那么,该类还定义成抽象类有意义吗?为什么?
可以没有抽象方法,有意义,是为了不让该类创建对象,方法可以直接让子类去使用
在实际开发中,有的时候,不希望使用者,直接实例化出一个类的对象,可以将这个类定义为 abstract

五,接口

1、 接口的概念

接口是功能的集合,同样可看做是一种特殊的数据类型,是比抽象类更为抽象的类。
接口只描述所应该具备的方法,并没有具体实现,具体的实现由接口的实现类(相当于接口的子类)来完成。这样将功能的定义与实现分离,优化了程序设计。
记住:一切事物均有功能,即一切事物均有接口

2、 接口的声明

使用 interface 代替了原来的 class 其他步骤与定义类相同

public interface 接口名称 extends 其他的类/接口名 {  
        //声明变量  
        //抽象方法  
}

接口是隐式抽象的,当声明一个接口的时候,不必使用abstract关键字。

3、接口的实现

当类实现接口的时候,类要实现接口中所有的方法。否则,类必须声明为抽象类。
类使用implements关键字实现接口.
一个类如果实现接口, 有两种操作方法:
实现类为非抽象类, 就需要重写接口中所有的抽象方法.
实现类为抽象类, 可以不重写接口中的抽象方法。

class 类 implements 接口1,接口2... {
//重写接口中方法
}
四、 接口中成员变量的特点
接口中无法定义普通的成员变量.
接口中定义的变量,必须有固定的修饰符修饰public static final ,所以接口中的变量也称之为常量,其值不能改变。
static 可以被类名、接口名直接调用
final 最终,固定住变量的值
public static final 在接口中可以省略不写,也可以选择性写,但是不代表没有.
接口中的成员变量必须显式初始化.

interface Demo { ///定义一个名称为Demo的接口。
    public static final int NUM = 3;// NUM的值不能改变
}

5、 接口中成员方法的特点

接口中的成员都是 public 的,不能指定其它的访问控制修饰符
接口中成员方法定义的固定格式: public abstract 返回值类型 方法名字(参数列表)
子类必须覆盖接口中所有的抽象方法后,子类才可以实例化,否则子类是一个抽象类。

6、 类和接口的多实现

类和接口的多实现是接口最重要的体现:解决多继承的弊端。将多继承这种机制在 java 中通过多实现完成了。
多继承的弊端:
多继承时,当多个父类中有相同功能时,子类调用会产生不确定性。
其实核心原因就是在于多继承父类中功能有主体,而导致调用运行时,不确定运行哪个主体内容。
为什么多实现能解决? 因为接口中的功能都没有方法体,都是由子类/实现类重写来明确

7、 接口的继承

一个接口能继承另一个接口,和类之间的继承方式比较相似。接口的继承使用extends关键字,子接口继承父接口的方法。
在Java中,类的多继承是不合法,但接口允许多继承。

8、 接口与类的区别:

接口不能用于实例化对象。
接口没有构造方法。
接口中所有的方法必须是抽象方法。
接口不能包含普通成员变量,除了 static 和 final 变量。
接口不是被类继承了,而是要被类实现。
接口支持多继承。

9、 抽象类和接口的区别

相同点

抽象类和接口都位于继承的顶端,用于被其他类实现或继承.
都不能直接实例化对象.
都包含抽象方法,其子类都必须覆盖这些抽象方法.
区别

抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。
抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final类型的。
接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。
一个类只能继承一个抽象类,而一个类却可以实现多个接口。
二者的选择:

优先选用接口,尽量少用抽象类;
需要定义子类的行为,又要为子类提供共性功能时才选用抽象类;

10、 接口中的方法是否可以被重载和覆盖?

重载: 对于接口中的方法重载,重载条件和普通类没有任何区别,只是重载的方法没有方法体
覆盖: 接口中的方法也可以覆盖(Override),但没有实际的意义,因为接口中不提供方法的实现

11、接口的新特性——默认方法和静态方法(jdk8之后)

默认方法

可以在不影响已有类的情况下,修改接口
可以有方法实现
父类中的默认方法可以被子接口继承
子接口可以覆盖父接口中的默认方法,甚至还可以把父接口中的默认方法覆盖为抽象方法
实现接口后,因为默认方法不是抽象方法,所以可以不重写,但是如果开发需要,也可以重写
默认方法使用default 关键字,只能通过接口实现类的对象来调用。
注意:默认方法的访问权限也是默认public
静态方法

可以有方法实现
可以直接通过接口名来访问
静态方法没有方法覆盖,因为静态方法没有运行时多态

interface Test{
    //这个是默认方法
    default String get(String aa){
        System.out.println("我是jdk1.8默认实现方法...");
        return "";
    }   
    //这个是静态方法    
    static void staticmethod(){
        System.out.println("我是静态方法");
    }
}

注意事项

接口默认方法、静态方法可以有多个。
默认方法通过实例调用,静态方法通过接口名调用。
default 默认方法关键字只能用在接口中。
默认方法可以被继承,如果继承了多个接口,多个接口都定义了多个同样的默认方法,实现类需要重写默认方法不然会报错。
静态方法不能被继承及覆盖,所以只被具体所在的接口调用。

12、 标记接口

标记接口是没有任何方法和属性的接口.
它仅仅表明它的类属于一个特定的类型,供其他代码来测试允许做一些事情。
标记接口作用:简单形象的说就是给某个对象打个标(盖个戳),使对象拥有某个或某些特权。
例如:

java.awt.event 包中的 MouseListener 接口继承的 java.util.EventListener 接口定义如下:
package  java.util; 
public  interface  EventListener  {

}

标记接口主要用于以下两种目的:
建立一个公共的父接口:
正如EventListener接口,这是由几十个其他接口扩展的Java API,你可以使用一个标记接口来建立一组接口的父接口。 例如:当一个接口继承了EventListener接口,Java虚拟机(JVM)就知道该接口将要被用于一个事件的代理方案。
向一个类添加数据类型:
这种情况是标记接口最初的目的,实现标记接口的类不需要定义任何接口方法(因为标记接口根本就没有方法),但是该类通过多态性变成一个接口类型。
posted @ 2020-12-23 14:03  XieYingpeng  阅读(207)  评论(0编辑  收藏  举报