day02_Java基础
文章目录
- day02_Java基础
- 一、今日课程内容
- 二、数组(熟悉)
- 1、定义格式
- 2、基本使用
- 3、了解数组的内存图介绍
- 4、数组的两个小问题
- 5、数组的常见操作
- 三、方法(熟悉)
- 1、定义格式
- 2、方法重载overload
- 四、面向对象(掌握)
- 1、类的定义与使用
- 2、构造方法
- 3、封装
- 4、继承
- 5、多态
- 6、两个关键字
- 7、抽象类
- 8、接口
- 9、内部类
- 五、作业
day02_Java基础
一、今日课程内容
-
1- 数组(熟悉)
-
2- 方法(熟悉)
-
3- 面向对象(掌握)
今日目的:面向对象
二、数组(熟悉)
和Python中一样, Java中也是有用来同时存储多个同类型元素的容器的, 那就是: 数组。在一个数组中,数据元素的类型是唯一的,即一个数组中的元素的类型相同。
1、定义格式
- 方式一: 动态初始化 (我们给定长度, 由系统给出默认初始化值)
格式一: 数据类型[] 数组名 = new 数据类型[长度];
格式二: 数据类型 数组名[] = new 数据类型[长度];
解释: 上述两种定义方式只是写法不同, 并无其他区别。推荐使用格式一
- 方式二: 静态初始化(我们给定初始化值, 由系统指定长度)
格式一:
数据类型[] 数组名 = new 数据类型[]{元素1, 元素2, 元素3};
格式二:
数据类型[] 数组名 = {元素1, 元素2, 元素3};
解释: 上述两种定义方式只是写法不同, 并无其他区别. 推荐使用格式二
数组中的每个元素都是有编号的, 且编号是从0开始的. 可以方便我们快速操作数组中的元素.
解释: 编号也叫索引(这个是最常见的念法), 下标、角标、index
注意: Java中的数组只有正向索引,没有负向索引。
数组中每个元素都有默认值.
例如:
int类型数组, 元素默认值是: 0
double类型的数组, 元素默认值是: 0.0
boolean类型的数组, 元素默认值是: false
String类型的数组, 元素默认值是: null
2、基本使用
- 1- 通过数组名[索引]的形式, 可以快速获取数组中的指定元素
格式: 数组名[索引]
例如:
int[] arr = {11, 22, 33};
System.out.println(arr[0]); //打印结果是: 11
- 2- 通过数组名[索引] = 值;的方式, 可以修改数组中的指定元素值.
格式:
数组名[索引] = 值;
//例如:
int[] arr = {11, 22, 33};
System.out.println(arr[1]); //打印结果是: 22
arr[1] = 222;
System.out.println(arr[1]); //打印结果是: 222
- 3- 通过数组名的length方法, 可以获取数组的长度
格式: 数组名.length
例如:
int[] arr = {11, 22, 33};
System.out.println(arr.length); //打印结果是: 3
案例实现:
package com.itheima;
import java.util.Arrays;
public class Test09 {
public static void main(String[] args) {
//1. 创建一个数组
// 方式一: 动态指定, 构建数组的时候, 只需要指定长度即可
int[] arr1 = new int[10];
//int arr2[] = new int[10];
// 方式二: 静态指定, 构建数组的时候, 直接指定数组中每一个元素的值
//double[] arr3 = new double[]{3.5,6.2,1.3,4.5};
double[] arr4 = {3.5,6.2,1.3,4.5};
//2. 执行相关的操作
// 2.1 如何获取数组中指定位置的元素: 在数组中, 通过索引下标的方式获取, 默认下标从0开始
int e1 = arr1[2];
System.out.println(e1);
double e2 = arr4[3];
System.out.println(e2);
//double e3 = arr4[4]; // 注意: 当获取超出数组范围的数据时候, 会报出如下的错误: ArrayIndexOutOfBoundsException
//System.out.println(e3);
// 2.2 如何为数组中元素进行重新赋值
arr1[2] = 10;
System.out.println(arr1[2]);
// 2.3 如何获取数组的长度:
int length = arr4.length;
System.out.println(length);
// 2.4 遍历数组
System.out.println("-------------------");
// for循环
for(int i = 0; i< arr4.length; i++){
System.out.println(arr4[i]);
}
// while循环
System.out.println("-------------------");
int i = 0;
while(i< arr4.length){
System.out.println(arr4[i]);
i++;
}
// 增强for: 迭代器 快捷键: iter + 回车 此种方式与Python中For循环有一些相似了
System.out.println("-------------------");
for (double v : arr4) {
System.out.println(v);
}
System.out.println("-------------------");
// 2.5 将arr4中数据进行反转操作
double[] arr5 = new double[arr4.length];
for(int j = arr4.length-1; j >= 0; j--){
arr5[arr4.length -1 - j] = arr4[j];
}
System.out.println(Arrays.toString(arr5)); // 将数组以字符串的形式打印出来
// 演示 小错误:
String[] arr6 = new String[10];
String e3 = arr6[2]; // 注意 e3 = Null
e3.concat("xxx'"); // 注意: 使用 null 调用API, 会报: NullPointerException (空指针异常)
}
}
3、了解数组的内存图介绍
内存是计算机中的重要原件,也是临时存储区域,作用是运行程序。我们编写的程序是存放在硬盘中的,在硬盘中的程序是不会运行的,必须放进内存中才能运行,运行完毕后会清空内存。
即: Java虚拟机要运行程序,必须要对内存进行空间的分配和管理。为了提高运算效率,就对空间进行了不同区域的划分,因为每一片区域都有特定的处理数据方式和内存管理方式。
JVM的内存划分
1.栈: 存储局部变量以及所有代码执行的.
局部变量: 指的是定义在方法中, 或者方法声明上的变量.
特点: 先进后出.
2.堆: 存储所有new出来的内容(即: 对象).
特点: 堆中的内容会在不确定的时间, 被GC(垃圾回收)回收.
3.方法区: 存储字节码文件的.
字节码文件: 指的是后缀名为.class的文件.
4.本地方法区:
和系统相关, 了解即可.
5.寄存器
和CPU相关, 了解即可.
以下为一个数组的内存图:
public class ArrayDemo03 {
public static void main(String[] args) {
int[] arr = new int[3];
//打印数组中的第一个元素, 值为: 0
System.out.println(arr[0]);
//[I@1540e19d
System.out.println(arr);
}
}
其中[I@1540e19d是arr数组的地址值
核心点: 当创建一个数组的时候, 由于数组是一个引用类型, 是一个对象, 首先会在堆空间中开辟一块空间, 将数组中数据放置到空间下, 此空间同时也会有一个唯一的地址指向这个空间, 此时变量也是指向此内存空间地址
4、数组的两个小问题
数组是我们在实际开发中用到的比较多的容器, 在使用它的时候, 很可能会遇到如下的两个问题:
- 1- 数组索引越界异常(ArrayIndexOutOfBoundsException)
产生原因: 访问了数组中不存在的索引
解决方案: 访问数组中存在的索引即可
- 2- 空指针异常(NullPointerException)
产生原因: 访问了空对象。即: 对象为空, 你还去调用它的一些方法, 肯定不行
解决方案: 给对象赋具体的值即可
5、数组的常见操作
数组是我们在实际开发中用到的比较多的一种容器, 它的常见操作如下:
-
遍历数组
-
获取数组的最值(最大值, 或者最小值)
-
反转数组
package com.itheima;
import java.util.Arrays;
public class Demo02_数组相关操作 {
public static void main(String[] args) {
//1. 创建一个数组
int[] arr1 = {1,5,6,2,8,4,9,10,0};
//2. 执行相关的操作
//2.1 遍历数组
for (int i : arr1) {
System.out.println(i);
}
// 2.2 获取数组最大值 和 最小值
int maxV = arr1[0];
int minV = arr1[0];
for (int i : arr1) {
if( maxV < i ) {
maxV = i;
}
if( minV > i ) {
minV = i;
}
}
System.out.println("最大值为:" + maxV);
System.out.println("最小值为:" + minV);
// 2.3 如何反转数组
int[] arr2 = new int[arr1.length];
for(int i = arr1.length -1; i >= 0 ; i--){
System.out.println(arr1[i]);
arr2[arr1.length-1-i] = arr1[i];
}
System.out.println(Arrays.toString(arr2));
}
}
package cn.itcast.object;
import java.util.Arrays;
/**
* 数组演示:定义和基本操作
*/
public class ArrayDemo {
public static void main(String[] args) {
/*
1-定义数组
Arrays.toString:数组工具类,用来将数组转成字符串形式
*/
// 动态定义
int[] arr1 = new int[5];
int arr2[] = new int[5];
double[] arr3 = new double[5];
System.out.println(Arrays.toString(arr1));
System.out.println(Arrays.toString(arr3));
// 静态定义
int[] arr4 = {1,3,5};
int[] arr5 = new int[]{2,4,6};
System.out.println(Arrays.toString(arr4));
// 数组的基本操作
// 获取对应索引的值
System.out.println(arr4[0]);
System.out.println(arr4[1]);
System.out.println(arr4[2]);
//System.out.println(arr4[3]);
// 给数组中某个索引进行赋值操作
arr4[1] = 6;
System.out.println(arr4[1]);
System.out.println(Arrays.toString(arr4));
// 获取数组的长度
System.out.println(arr4.length);
System.out.println("------------------------------");
// 遍历数组。常规for循环的快捷方式:.for+回车
for(int i=0;i<arr4.length;i++){
System.out.println(arr4[i]);
}
int i=0;
while(i<arr4.length){
System.out.println(arr4[i]);
i++;
}
int minValue = Integer.MAX_VALUE;
int maxValue = Integer.MIN_VALUE;
// int maxValue = arr4[0];
// 获取数组的最值(最大值, 或者最小值)
for(int j=0;j<arr4.length;j++){
int tmp = arr4[j];
if(tmp>maxValue){
maxValue = tmp;
}
if(tmp<minValue){
minValue = tmp;
}
}
System.out.println(minValue);
System.out.println(maxValue);
// 反转数组
int[] newArr = new int[arr4.length];
for(int j=0;j<arr4.length;j++) {
newArr[j] = arr4[arr4.length-j-1];
}
System.out.println(Arrays.toString(newArr));
}
}
三、方法(熟悉)
Python中的函数, 是将具有独立功能的代码块组织成为一个整体,使其成为具有特殊功能的代码集。Java中也是如此。只不过,Java中的函数也叫方法.
1、定义格式
修饰符 返回值的数据类型 方法名(参数类型 参数名1, 参数类型 参数名2) { //这里可以写多个参数
//方法体;
return 具体的返回值;
}
修饰符: 目前记住这里是固定格式public static
返回值的数据类型: 用于限定返回值的数据类型的.
注意:
1.返回值的数据类型是int类型, 则说明该方法只能返回int类型的整数.
2.如果方法没有具体的返回值, 则返回值的数据类型要用void来修饰,不能省略.
方法名: 方便我们调用方法. 定义的时候 要遵循小驼峰的命名法
参数类型: 用于限定调用方法时传入的数据的数据类型.
例如: 参数类型是String类型, 说明我们调用方法时, 只能传入字符串.
参数名: 用于接收调用方法时传入的数据的变量.
方法体: 完成特定功能的代码。不建议一个方法的内部代码行数过多。
return 返回值: 用来结束方法的, 并把返回值返回给调用者.
解释: 如果方法没有明确的返回值, 则return关键字可以省略不写.
注意事项:
1.方法与方法之间是平级关系, 不能嵌套定义.
2.方法的定义和调用没有先后顺序的要求
3.方法自身不会直接运行, 而是需要我们手动调用方法后, 它才会执行, 该过程称为方法调用.
4.方法的功能越单一越好.
5.定义方法的时候写在参数列表中的参数, 都是: 形参.
形参: 形容调用方法的时候, 需要传入什么类型的参数.
6.调用方法的时候, 传入的具体的值(变量或者常量都可以), 叫实参.
实参: 调用方法时, 实际参与运算的数据.
示例:
需求一: 编写一个方法, 接收数组参数, 实现遍历数组的功能
需求二: 获取数组最值(例如: 最大值)
package com.itheima;
public class Test10_method {
// main 方法
public static void main(String[] args) {
int[] arr1 = {1,4,6,3,8,10,15};
// 调用方法
foreachArr(arr1); // 传入的参数为实参
// 获取数组中最大值
int maxNum = getMaxNum(arr1);
System.out.println("最大值为:"+maxNum);
}
// 方法的定义
public static void foreachArr(int[] arr){ // 定义参数为形参
// 遍历数组
for (int e : arr) {
System.out.println(e);
}
}
// 定义方法: 实现获取数组中最大值
public static int getMaxNum(int[] arr){
int max = arr[0];
for (int e : arr) {
if(e > max) max = e;
}
return max;
}
}
2、方法重载overload
补充:重写override
同一个类中, 出现方法名相同, 但是参数列表不同的两个或以上的方法时**称为方法重载. **方法重载与方法的返回值的数据类型无关.
注意: 参数列表不同分为两种情况
1.参数的个数不同.
2.对应参数的数据类型不同.
方法重载的总结:
只关注方法名称 和 形参列表,与返回值类型没关系
要求方法名称必须相同,而形参列表不同。这里的不同不包括形参的名称
需求
演示方法的重载
package com.itheima.day02;
public class 方法的重载 {
public static void main(String[] args) {
System.out.println(myAdd(1, 2));
System.out.println(myAdd(1.99, 2.0));
}
/*
方法重载的总结:
只关注方法名称 和 形参列表,与返回值类型没关系
要求方法名称必须相同,而形参列表不同。这里的不同不包括形参的名称
*/
public static int myAdd(int num1,int num2){
return num1+num2;
}
// 这种不叫方法的重载
// public static int myAdd(int num1,int num2222){
// return num1+num2222;
// }
public static double myAdd(double num1,double num2){
return num1+num2;
}
}
四、面向对象(掌握)
和Python一样, Java也是一门以面向对象为编程思想的语言, 也有类, 对象的概念, 并且面向对象的三大特征: 封装, 继承, 多态, 在Java中也都是有的, 接下来, 我们来研究下Java中的面向对象应该如何运用.
1、类的定义与使用
定义类其实就是定义类的成员(成员属性/变量和成员方法)
-
成员变量:
- 1- 和以前定义变量是一样的, 只不过位置发生了改变, 写到类中, 方法外
- 2- 而且成员变量还可以不用赋值, 因为它有默认值.
-
成员方法:
- 1- 和以前定义方法是一样的, 只不过把static关键字去掉.
- 2- 这点先记忆即可, 后面我们再讲解static关键字的用法.
定义类的格式:
public class 类名 {
//成员变量, 私有化, 类似于Python中的 __
//构造方法, 空参, 全参, 类似于Python的魔法方法之 __init__(self)
//getXxx(), setXxx()方法, 用来获取或者设置对象的属性值的
//成员方法, 就是普通的函数
}
使用类中成员的格式:
1.创建该类的对象, 格式如下:
类名 对象名 = new 类名();
2.通过对象名.的形式, 调用类中的指定成员即可, 格式如下:
//成员变量
对象名.成员变量
//成员方法
对象名.成员方法(参数列表中各数据类型对应的值...)
需求:
1- 定义手机类Phone.
2- 创建测试类PhoneTest, 在类中定义main方法, 并访问手机类(Phone类)中的成员.
属性: 品牌(brand), 价格(price), 颜色(color)
行为: 打电话(call), 发短信(sendMessage)
非私有方案:
package com.itheima.类的使用;
public class Phone {
// 成员变量
String brand;
double price;
String color;
public void call(){
System.out.println("打电话");
}
public void sendMessage(){
System.out.println("发短信");
}
}
// 测试类
package com.itheima.类的使用;
public class Demo05_phoneTest {
public static void main(String[] args) {
//1. 创建 手机类的对象 构建对象 关键词 new
Phone phone = new Phone();
//2. 为成员属性赋值:
phone.brand = "华为";
phone.color = "祖母绿";
phone.price = 19999.0;
//3. 获取成员属性的值
System.out.println(phone.brand);
System.out.println(phone.color);
System.out.println(phone.price);
//4. 调用成员方法
phone.call();
phone.sendMessage();
}
}
(推荐)私有化方案:
package com.itheima.类的使用;
/*
权限修饰符
public: 公共的 一旦方法 或者 类 成员变量 被标记, 可以在其他类中, 甚至其他的项目中 都可以加载到此内容
private: 私有的 表示 仅在当前类中可用
*/
public class Phone2 {
// 成员变量 私有化
private String brand;
private double price;
private String color;
// 为私有的属性 提供 getter 和 setter方法
// 快捷键: alt + ins 键
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public void call(){
System.out.println("打电话");
}
public void sendMessage(){
System.out.println("发短信");
}
}
// 测试类
package com.itheima.类的使用;
public class Demo06_phone2Test {
public static void main(String[] args) {
//1. 创建 手机类的对象 构建对象 关键词 new
Phone2 phone2 = new Phone2();
//2. 为成员属性赋值
phone2.setBrand("三星");
phone2.setColor("土豪金");
phone2.setPrice(5888);
//3. 获取成员属性的值
System.out.println(phone2.getColor());
System.out.println(phone2.getBrand());
System.out.println(phone2.getPrice());
//4. 调用方法
phone2.call();
phone2.sendMessage();
}
}
2、构造方法
Java中的构造方法类似于Python中的魔法方法之_init_(), 就是用来创建对象的, 捎带着可以给对象的各个成员变量赋值.
大白话:
构造方法就是用来快速对对象的各个属性赋值的.
格式:
- 1.构造方法名必须和类名完全一致(包括大小写).
- 2.构造方法没有返回值类型, 连void都不能写.
- 3.构造方法没有具体的返回值, 但是可以写return(实际开发, 一般不写 基本没见过写的).
public 类名(参数类型 参数名1, 参数类型 参数名2) { //这里可以写多个参数.
//给对象的各个属性赋值即可.
}
演示构造的使用:
package com.itheima.类的使用;
public class Stu {
// 当不写构造方法的时候, 类会默认提供一个空参的构造, 一旦我们书写了构造方法, 默认的构造就不提供了, 如果需要空参的构造, 必须手动写出来
// 快捷键: alt + ins
public Stu() {
}
public Stu(String name, int age) {
this.name = name;
this.age = age;
}
public Stu(String name) {
this.name = name;
}
public Stu(int age) {
this.age = age;
}
// 属性: 当创建对象的时候,不管是否有赋值操作, 先默认给成员属性进行初始化操作
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
package com.itheima.类的使用;
public class Demo07_stuTest {
public static void main(String[] args) {
//1, 创建Stu对象
//Stu stu = new Stu("张三",30);
//Stu stu = new Stu();
//Stu stu = new Stu(50);
Stu stu = new Stu("李四");
//2. 赋值
//stu.setAge(50);
//3. 获取值
System.out.println(stu.getAge());
System.out.println(stu.getName());
}
}
相关的注意事项:
1.如果我们没有给出构造方法, 系统将给出一个默认的无参构造供我们使用.
2.如果我们给出了构造方法, 系统将不再提供默认的构造方法给我们使用.
2.1 这个时候, 如果我们还想使用无参构造, 就必须自己提供.
2.2 建议定义类时,我们给出无参构造, 方便用户调用(实际开发都这么做的).
3- 如果需要将这个类构建为一个工具类, 此时这个类的构造方法必须私有化(不允许用户创建这个类对象)
4- Java中一个类里面能够有多个构造方法
3、封装
封装是面向对象编程思想的三大特征之一, 所谓的封装指的就是隐藏对象的属性和实现细节, 仅对外提供一个公共的访问方式.
记忆:
面向对象的三大特征: 封装, 继承, 多态.
问题一: 怎么隐藏?
答案: 通过private关键字实现.
问题二: 公共的访问方式是什么?
答案: getXxx()和setXxx()方法
可能涉及的相关特殊关键词:
- 1- private关键字
private是一个关键字, 也是访问权限修饰符的一种, 它可以用来修饰类的成员(成员变量和成员方法).
特点: 被private修饰的内容(成员变量和成员方法)只能在本类中直接使用,外界无法直接访问。
应用场景:
1.在实际开发中, 成员变量基本上都是用private关键字来修饰的.
2.如果明确知道类中的某些内容不想被外界直接访问, 都可以通过private来修饰.
- 2- this关键字
this代表本类当前对象的引用, 大白话翻译: 谁调用, this就代表谁.
作用:
用来解决局部变量和成员变量重名问题的.
类似于python中 self 关键词
4、继承
多个类中存在相同属性和方法时, 将这些内容抽取到单独的一个类中, 那么这多个类就无需再定义这些属性和行为了, 只要继承那个类即可. 这个关系, 就叫继承, Java中, 类与类之间的继承只能单继承,不能多继承,但是可以多层继承.也就是现实生活中,一个人只有一个父亲,也就是单继承;但是还有爷爷,也就是多层继承。
格式:
public class 类A extends 类B { //子承父业
}
解释:
类A: 叫子类, 或者派生类.
类B: 叫父类, 基类, 或者超类.
我们一般会念做: 子类和父类.
继承关键字是extends
需求:
-
1.按照标准格式定义一个人类(Person类), 属性为姓名和年龄.
-
2.定义老师类(Teacher), 继承自人类, 并在老师类中定义teach()方法.
-
3.定义学生类(Student), 继承自人类, 并在学生类中定义study()方法.
-
4.在PersonTest测试类的main方法中, 分别创建老师类和学生类的对象, 并调用各自类中的成员.
person类:
package com.itheima.继承;
public class Person {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void eat(){
System.out.println("人人都要吃饭...");
}
}
学生类:
package com.itheima.继承;
public class Student extends Person {
public void study(){
System.out.println(super.getName()+"学生要上课, 课上不能睡觉......");
}
}
老师类:
package com.itheima.继承;
public class Teacher extends Person {
public void teach(){
System.out.println(super.getName()+"老师,今年已经"+super.getAge()+"岁了, 依然也要教书......");
}
}
测试类
package com.itheima.继承;
public class Demo08_PersonTest {
public static void main(String[] args) {
//1. 创建 学生类 和 老师类 对象
Student stu = new Student();
Teacher teacher = new Teacher();
//2. 设置成员属性
stu.setName("张三");
stu.setAge(20);
teacher.setName("张教授");
teacher.setAge(40);
//3. 获取值, 调用方法
System.out.println(stu.getAge());
System.out.println(stu.getName());
stu.study();
stu.eat();
System.out.println(teacher.getAge());
System.out.println(teacher.getName());
teacher.teach();
teacher.eat();
}
}
子类在继承父类的时候, 可以将父类中公共(public)的方法 和 属性 全部都继承, 如果父类中属性和方法被标记为私有化后, 我们无法继承
说明: 继承的好处和弊端
好处:
1.提高了代码的复用性.
2.提高了代码的可维护性.
3.让类与类之间产生关系, 是多态的前提.
弊端:
让类与类之间产生了关系, 也就让类的耦合性增强了.
解释:
开发原则: 高内聚, 低耦合.
内聚: 指的是类自己独立完成某些事情的能力.
耦合: 指的是类与类之间的关系.
方法的重写:
子类中出现和父类一模一样的方法时, 称为方法重写. 方法重写要求返回值的数据类型也必须一样
方法名称、形参列表(参数个数、参数顺序、参数数据类型)、方法返回值数据类型都需要一样。另外子类方法的访问范围要大于等于父类的范围
什么时候需要使用方法重写:
当子类需要使用父类的功能, 而功能主体又有自己独有需求的时候, 就可以考虑重写父类中的方法了, 这样, 即沿袭了父类的功能, 又定义了子类特有的内容.
需求:
-
1 定义Phone类, 并在类中定义call(String name)方法.
-
2 定义NewPhone类, 继承Phone类, 然后重写call(String name)方法.
-
3 在PhoneTest测试类中, 分别创建两个类的对象, 然后调用call()方法, 观察程序执行结果.
phone类:
package com.itheima.继承;
public class Phone {
public void call(String name){
System.out.println(name + "打电话");
}
}
newPhone类:
package com.itheima.继承;
public class NewPhone extends Phone {
@Override // 此注解表示是当前这个方法是一个重写的方法(仅仅是标记)
public void call(String name) {
super.call(name);
System.out.println(name+"打电话,并且同时视频聊天");
}
}
测试类
package com.itheima.继承;
public class Demo09_PhoneTest {
public static void main(String[] args) {
//1. 创建 NewPhone类对象
NewPhone newPhone = new NewPhone();
newPhone.call("张三");
System.out.println("=================");
Phone phone = new Phone();
phone.call("李四");
}
}
注意事项:
1.子类重写父类方法时, 方法声明上要用@Override注解来修饰.
2.父类中私有的方法不能被重写.
3.子类重写父类方法时, 访问权限不能更低.
子类可以重写父类中公共的方法, 可以针对父类的方法进行增强
5、多态
多态指的是同一个事物(或者对象)在不同时刻表现出来的不同状态.
例如: 一杯水.
• 常温下是液体.
• 高温下是气体.
• 低温下是固体.
但是水还是那杯水, 只不过在不同的环境下, 表现出来的状态不同.
多态使用的前提条件:
1.要有继承关系.
2.要有方法重写.
3.要用父类引用指向子类对象.
简单理解: 父类接收子类对象 (父类指向子类的引用)
示例:
-
1 定义动物类Animal, 并在类中定义一个成员方法: eat()
-
2 定义猫类Cat, 继承Animal类, 并重写eat()方法.
-
2 定义猫类Dog, 继承Animal类, 并重写eat()方法.
-
3 在AnimalTest测试类的main方法中, 通过多态的方式创建猫类和狗类对象.
-
4 通过类对象, 调用eat()方法.
package com.itheima.多态;
public class Animal {
public void eat(){
System.out.println("动物都要吃......");
}
}
package com.itheima.多态;
public class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗爱吃骨头......");
}
}
package com.itheima.多态;
public class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫爱吃鱼......");
}
}
package com.itheima.多态;
public class Demo10_AnimalTest {
// 多态格式: 父类 = 子类对象
// 多态特性: 编译看左边, 运行看右边
public static void main(String[] args) {
//1. 创建一个猫对象
Animal cat = new Cat();
cat.eat();
//2. 创建一个狗 对象
Animal dog = new Dog();
dog.eat();
}
}
多态的好处和弊端
好处: 提高了程序的扩展性.
弊端:
父类引用不能访问子类的特有功能.
问: 那如何解决这个问题呢?
答: 通过向下转型来解决这个问题. (强制类型转换)
上下转型的方式:
向上转型:
格式
父类型 对象名 = new 子类型();
例如: Animal an = new Cat();
向下转型:
格式
子类型 对象名 = (子类型)父类引用;
例如: Cat c = (Cat)an;
示例:
// 向下转型
Animal animal3 = new Cat();
Cat cat = (Cat) animal3; // 向下转型
Dog dog = (Dog) animal3; // 直接报错(ClassCastException 类型转换异常), 因为本身就不是 Dog类型, 所以无法进行向下转型
cat.seize();
System.out.println(animal3);
说明: 向下转型的目的是为了调用子类中特有的方法
package com.itheima.多态;
public class Demo10_AnimalTest {
// 多态格式: 父类 = 子类对象
// 多态特性: 编译看左边, 运行看右边
public static void main(String[] args) {
//1. 创建一个猫对象
Animal cat = new Cat();
cat.eat();
//2. 创建一个狗 对象
Animal dog = new Dog();
dog.eat();
//3. 调用 execute 方法
execute(new Dog());
}
public static void execute(Animal animal){
animal.eat();
// 判断, 如果传入的类型是 Dog, 请强转为 dog类型, 调用action方法
// 关键词: instanceof 类型判断 , 格式: 对象 instanceof 需要判断类型 返回 True表示是这个类型的对象, 返回Flase表示不是这个类型的对象
if( animal instanceof Dog ){
Dog dog = (Dog) animal;
dog.action();
}
}
}
6、两个关键字
- 1- final关键字
final是一个关键字, 表示最终的意思, 可以修饰类, 成员变量, 成员方法.
•修饰的类: 不能被继承, 但是可以继承其他的类. 称为 最终类
•修饰的变量: 是一个常量, 只能被赋值一次. 称为 常量
•修饰的方法: 不能被子类重写. 既需要别人调用, 又不想让别人重写内部内容, 需要添加final
需求
-
1 定义Father类, 并定义它的子类Son.
-
2 先用final修饰Father类, 看Son类是否还能继承Father类.
-
3 在Son类中定义成员变量age, 并用final修饰, 然后尝试给其重新赋值, 并观察结果.
-
4 在Father类中定义show()方法, 然后用final修饰, 看Son类是否能重写该方法.
package com.itheima.两个关键词;
// 如果使用final 修饰类, 此类为最终类, 无法被其他类所继承, 但是可以继承其他类
public /*final */ class Father {
// 既想让其他用户使用, 又不想让其他用户改写我的函数, 此时可以将函数/方法 使用 final修饰
public final void show(){
System.out.println("xxxxxxxxxxxxxxxxxx");
}
}
package com.itheima.两个关键词;
public class Son extends Father {
final int AGE = 18; // 一旦被final所修饰, 当前这个变量 只能赋值 1次 称为常量
/*@Override
public void show() {
System.out.println("xxxxxxx");
}*/
}
package com.itheima.两个关键词;
public class Demo12_final {
public static void main(String[] args) {
Son son = new Son();
//son.age = 18;
System.out.println(son.AGE);
}
}
- 2- static关键字
static是一个关键字, 表示静态的意思, 可以修饰成员变量, 成员方法. 但是不能修饰class类
特点:
1.随着类的加载而加载. (当class文件被加载到JVM方法区的时候, 被static修饰的成员, 也会直接加载, 直接使用)
2.优先于对象存在.
3.被static修饰的内容, 能被该类下所有的对象共享.
解释: 这也是我们判断是否使用静态关键字的条件.
4.可以通过 类名. 的形式调用.也就是你不需要创建类的实例对象
静态方法的访问特点及注意事项
访问特点
静态方法只能访问静态的成员变量和静态的成员方法.
简单记忆: 静态只能访问静态.
注意事项
1.在静态方法中, 是没有this, super关键字的.
2.因为静态的内容是随着类的加载而加载, 而this和super是随着对象的创建而存在.
即: 先进内存的, 不能访问后进内存的.
需求:
-
1 定义学生类, 属性为姓名和年龄(静态修饰), 非静态方法show1(),show2(), 静态方法show3(), show4().
-
2 尝试在show1()方法中, 调用: 姓名, 年龄, show2(), show4()
-
3 尝试在show3()方法中, 调用: 姓名, 年龄, show2(), show4().
package com.itheima.两个关键词;
public class Student3 {
String name;
static int age;
public void show1(){ // 在 非静态的方法中, 可以使用静态内容 也可以使用非静态的内容
System.out.println("我是show1...."+name +" "+ age);
show2(); // 调用非静态的方法
show4(); // 调用静态的方法
}
public void show2(){
System.out.println("我是show2....");
}
public static void show3(){ // 在静态方法的中, 可以使用静态的成员, 但是不能使用非静态的成员
System.out.println("我是show3...."+ age);
//show2();
show4();
}
public static void show4(){
System.out.println("我是show4....");
}
}
静态代码块: 通过 static 对代码块进行修饰, 修饰后 称为 静态代码块
特点: 随着类的加载而加载, 而且一般只会加载一次
放置的位置: 类中 方法外
package com.itheima.两个关键词;
public class Teacher {
String name;
String address;
int age;
// 随着类的加载而加载, 而且一般只会加载一次
// 基于静态代码块, 可以进行初始化的操作。例如:数据库连接的创建
static{
System.out.println("上课前, 需要进行备课...");
}
public void teach(){
System.out.println("老师要上某一阶段的课");
}
}
范围的修饰符(访问范围由大到小排序):public > protected > 没有修饰符default > private
7、抽象类
回想前面我们的猫狗案例, 提取出了一个动物类, 这个时候我们可以通过Animal an = new Animal(); 来创建动物对象, 其实这是不对的, 因为, 我说动物, 你知道我说的是什么动物吗? 只有看到了具体的动物, 你才知道, 这是什么动物. 所以说, 动物本身并不是一个具体的事物, 而是一个抽象的事物. 只有真正的猫, 狗才是具体的动物.
同理, 我们也可以推想, 不同的动物吃的东西应该是不一样的, 所以, 我们不应该在动物类中给出具体的体现, 而是应该给出一个声明即可. 在Java中, 一个没有方法体的方法应该定义为抽象方法, 而类中如果有抽象方法, 该类必须定义为抽象类。抽象类中不是必须要有抽象方法.
**抽象类的使用场景:用来定义大多数子类都具备的规则,只不过具体的实现思路每个子类都是不一样的。**因为抽象类需要在下面创建对应的实现子类。
示例需求:
- 1- 创建抽象类Animal.
- 2- 在该类中定义抽象方法eat()
package com.itheima.抽象类;
// 说明: 有抽象方法的类 一定是一个抽象类, 在抽象类中可以有非抽象的方法
// 抽象类 和 抽象 方法 都使用 abstract关键词标注
public abstract class Animal {
public abstract void eat();
public void run(){
System.out.println("xxxxx");
}
}
package com.itheima.抽象类;
public class Demo01_AnimalTest {
public static void main(String[] args) {
// 抽象类, 是无法被实例化的(无法创建对象)
//Animal animal = new Animal();
}
}
抽象类的特点:
抽象类和抽象方法必须用abstract关键字修饰.
抽象类中不一定有抽象方法, 有抽象方法的类一定是抽象类
抽象类不能实例化.
– 那抽象类如何实例化呢?
– 可以通过多态的方式, 创建其子类对象, 来完成抽象类的实例化. 这也叫: 抽象类多态.
抽象类的子类:
– 如果是普通类, 则必须重写父抽象类中所有的抽象方法.
– 如果是抽象类, 则可以不用重写父抽象类中的抽象方法.
需求:
- 定义抽象类Animal , 类中有一个抽象方法eat(), 还有一个非抽象方法sleep().
- 尝试在测试类中, 创建Animal类的对象, 并观察结果.
- 创建普通类Cat, 继承Animal类, 观察是否需要重写Animal#eat()方法.
- 创建抽象类Dog, 继承Animal类, 观察是否需要重写Animal#eat()方法.
package com.itheima.抽象类;
// 说明: 有抽象方法的类 一定是一个抽象类, 在抽象类中可以有非抽象的方法
// 抽象类 和 抽象 方法 都使用 abstract关键词标注
public abstract class Animal {
public abstract void eat();
public void sleep(){
System.out.println("动物都要睡觉...");
}
}
package com.itheima.抽象类;
public class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼......");
}
}
package com.itheima.抽象类;
public class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗吃骨头......");
}
}
package com.itheima.抽象类;
public class Demo01_AnimalTest {
public static void main(String[] args) {
// 抽象类, 是无法被实例化的(无法创建对象)
//Animal animal = new Animal();
// 创建实例对象
Dog dog = new Dog();
dog.eat();
dog.sleep();
Cat cat = new Cat();
cat.eat();
cat.sleep();
// 多态的方式
Animal animal_1 = new Dog();
Animal animal_2 = new Cat();
animal_1.eat(); // 编译看左边, 运行看右边
animal_1.sleep();
}
}
8、接口
继续回到我们的猫狗案例,我们想想狗一般就是看门,猫一般就是作为宠物了。但是,现在有很多的驯养员或者是驯兽师,可以训练出:猫钻火圈,狗跳高,狗做计算等。而这些额外的动作,并不是所有猫或者狗一开始就具备的,这应该属于经过特殊的培训训练出来的。所以,这些额外的动作定义到动物类中就不合适,也不适合直接定义到猫或者狗中,因为只有部分猫狗具备这些功能。
所以,为了体现事物功能的扩展性,Java中就提供了接口来定义这些额外功能,并不给出具体实现,将来哪些猫狗需要被训练,只需要这部分猫狗把这些额外功能实现即可。
接口中的内容, 都是抽象的, 一般是用于定义规则 协议
接口的使用场景:接口是一种特殊的抽象类。用来定义规则的,但是这些规则只有少部分类才具备的特点。
接口的特点:
1.接口用interface关键字修饰.
2.类和接口之间是实现关系, 用implements关键字表示.
3.接口不能实例化.
– 那接口如何实例化呢?
– 可以通过多态的方式, 创建其子类对象, 来完成接口的实例化. 这也叫: 接口多态.
4.接口的子类:
– 如果是普通类, 则必须重写父接口中所有的抽象方法.
– 如果是抽象类, 则可以不用重写父接口中的抽象方法.
需求:
1 定义Jumpping接口, 接口中有一个抽象方法jump().
2 定义Cat类, 实现Jumpping接口, 重写jump()方法.
3 在测试类的main方法中, 创建Jumpping接口对象, 并调用其jump()方法
package com.itheima.接口;
public interface Jumpping {
public void jump();
}
package com.itheima.接口;
public class Cat implements Jumpping {
@Override
public void jump() {
System.out.println("猫可以钻火圈......");
}
}
package com.itheima.接口;
public class Demo02_jumppingTest {
public static void main(String[] args) {
Jumpping jumpping = new Cat();
jumpping.jump();
}
}
类与接口之间的关系
- 类与类之间: 继承关系, 只能单继承, 不能多继承, 但是可以多层继承.
- 类与接口之间: 实现关系, 可以单实现, 也可以多实现. 还可以在继承一个类的同时实现多个接口.
- 接口与接口之间: 继承关系, 可以单继承、多层继承, 也可以多继承.
综合案例:
- 已知有乒乓球运动员(PingPangPlayer)和篮球运动员(BasketballPlayer), 乒乓球教练(PingPangCoach)和篮球教练(BasketballCoach).
- 他们都有姓名和年龄, 都要吃饭, 但是吃的东西不同.
- 乒乓球教练教如何发球, 篮球教练教如何运球和投篮.
- 乒乓球运动员学习如何发球, 篮球运动员学习如何运球和投篮.
- 为了出国交流, 跟乒乓球相关的人员都需要学习英语.
package com.itheima.接口综合案例;
public abstract class Person {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public abstract void eat();
}
package com.itheima.接口综合案例;
public abstract class Coach extends Person {
public abstract void teach();
}
package com.itheima.接口综合案例;
public class BasketballCoach extends Coach {
@Override
public void teach() {
System.out.println("篮球教练教如何运球和投篮");
}
@Override
public void eat() {
System.out.println("篮球教练喜欢吃鱼肉......");
}
}
package com.itheima.接口综合案例;
public class PingPangCoach extends Coach implements Language{
@Override
public void teach() {
System.out.println("乒乓球教练教如何发球");
}
@Override
public void eat() {
System.out.println("乒乓球教练喜欢吃高蛋白视频: 比如 鸡蛋");
}
@Override
public void studyLanguage() {
System.out.println("学习英语");
}
}
package com.itheima.接口综合案例;
public abstract class Player extends Person {
public abstract void study();
}
package com.itheima.接口综合案例;
public class BasketballPlayer extends Player{
@Override
public void study() {
System.out.println("篮球运动员学习如何运球和投篮......");
}
@Override
public void eat() {
System.out.println("篮球运动员喜欢吃日料......");
}
}
package com.itheima.接口综合案例;
public class PingPangPlayer extends Player implements Language {
@Override
public void study() {
System.out.println("乒乓球运动员学习如何发球");
}
@Override
public void eat() {
System.out.println("乒乓球运动员喜欢吃麻辣烫......");
}
@Override
public void studyLanguage() {
System.out.println("学习英语");
}
}
package com.itheima.接口综合案例;
public interface Language {
public void studyLanguage();
}
9、内部类
类里边还有一个类, 外边那个类叫做外部类, 里边那个类叫做内部类,Java中不允许方法的嵌套定义。
示例:
public class A {
public class B{
}
}
A: 外部类
B: 内部类
内部类的分类:
- 1- 成员内部类(了解)
public class A { //外部类
public class B{ //成员内部类
}
}
- 2- 局部内部类(了解)
public class A { //外部类
//外部类的成员方法
public void show() {
//局部内部类
class B {
}
}
}
-
3- 匿名内部类(重要)
- 匿名内部类指的就是没有名字的局部内部类.
- 格式:
xxx = new 类名或者接口名() { //重写类或者接口中所有的抽象方法 };
- 本质
匿名内部类就是一个继承了类或者实现了接口的匿名的子类对象. 简单理解: 匿名内部类的本质就是一个子类对象.
- 应用场景
•当对对象方法(即: 成员方法)仅调用一次的时候. •匿名内部类可以作为方法的实参进行传递.
-
案例需求:
定义Animal抽象类, 该类中有一个抽象方法eat().
在测试类的main方法中, 通过匿名内部类的形式创建Animal抽象类的子类对象.
调用Animal类中的eat()方法
package com.itheima.内部类; public abstract class Animal { public abstract void eat(); } package com.itheima.内部类; public class Demo03_AnimalTest { // 匿名内部类: 将匿名内部类作为抽象类的子类对象 public static void main(String[] args) { //1. 创建Animal的对象 // Animal animal = new Animal(); // 抽象类不能实例化对象 Animal animal = new Animal(){ @Override public void eat() { System.out.println("吃东西"); } }; //animal.eat(); execute(animal); } public static void execute(Animal animal){ animal.eat(); } } package com.itheima.内部类; public interface Jumpping { public void jump(); } package com.itheima.内部类; public class Demo04_JumppingTest { // 匿名内部类: 将匿名内部类作为接口的子类对象 public static void main(String[] args) { // Jumpping jumpping = new Jumpping(); 接口不能创建实例对象 Jumpping jumpping = new Jumpping() { @Override public void jump() { System.out.println("钻火圈..."); } }; jumpping.jump(); } } package com.itheima.内部类; import com.itheima.抽象类.Animal; public class Cat { public void eat() { System.out.println("猫吃鱼......"); } } package com.itheima.内部类; public class Demo05_CatTest { // 匿名内部类, 作为类的子类对象 public static void main(String[] args) { Cat cat = new Cat(){ @Override public void eat() { System.out.println("猫吃猫粮......"); } }; cat.eat(); } }
建议: 当接口或者抽象类中的抽象方法在3个(含)以下时,并且只需要使用一次的时候, 就可以考虑使用匿名内部类的形式来创建对象了
五、作业
作业一: 场景题目:
有以下几个类, 分别为 人类 学生类 老师类 , 学生类和老师类需要继承人类
在人类有以下成员:
成员属性: 姓名 年龄 性别 地址 生日
成员方法: 吃饭 跑步
在学生类:
需要重写吃饭的方法, 学生爱吃麻辣烫
属性: 职业: 学生 (固定值)
在教师类:
需要重写吃饭的方法, 老师爱吃米饭
属性: 职业: 教师(固定值)
创建一个测试类,
创建三个学生类
分别为:
张三 18 男 ...
李四 17 女 ...
王五 19 男 ...
并将三个学生对象, 放置到数组中
创建三个教师类:
分别为: 自己写 随意
并将三个老师对象, 放置到数组中
对学生数组对象, 进行反转操作, 反转后, 通过Arrays将其打印出来
对教师数组对象, 采用增强for循环, 遍历打印
提示: 在类中,可以重写一个 toString的方法 此方法可以将对象的数据打印, 而不是打印对象的地址值