文章目录
- 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 = Nulle3.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];//打印数组中的第一个元素, 值为: 0System.out.println(arr[0]); //[I@1540e19dSystem.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. 创建 手机类的对象 构建对象 关键词 newPhone 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. 创建 手机类的对象 构建对象 关键词 newPhone2 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 + inspublic 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 {@Overridepublic void eat() {System.out.println("狗爱吃骨头......");}
}package com.itheima.多态;public class Cat extends Animal {@Overridepublic 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次 称为常量/*@Overridepublic 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 {@Overridepublic void eat() {System.out.println("猫吃鱼......");}
}package com.itheima.抽象类;public class Dog extends Animal {@Overridepublic 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 {@Overridepublic 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 {@Overridepublic void teach() {System.out.println("篮球教练教如何运球和投篮");}@Overridepublic void eat() {System.out.println("篮球教练喜欢吃鱼肉......");}
}package com.itheima.接口综合案例;public class PingPangCoach extends Coach implements Language{@Overridepublic void teach() {System.out.println("乒乓球教练教如何发球");}@Overridepublic void eat() {System.out.println("乒乓球教练喜欢吃高蛋白视频: 比如 鸡蛋");}@Overridepublic 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{@Overridepublic void study() {System.out.println("篮球运动员学习如何运球和投篮......");}@Overridepublic void eat() {System.out.println("篮球运动员喜欢吃日料......");}
}package com.itheima.接口综合案例;public class PingPangPlayer extends Player implements Language {@Overridepublic void study() {System.out.println("乒乓球运动员学习如何发球");}@Overridepublic void eat() {System.out.println("乒乓球运动员喜欢吃麻辣烫......");}@Overridepublic 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(){@Overridepublic 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() {@Overridepublic 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(){@Overridepublic void eat() {System.out.println("猫吃猫粮......");}};cat.eat();} }
建议: 当接口或者抽象类中的抽象方法在3个(含)以下时,并且只需要使用一次的时候, 就可以考虑使用匿名内部类的形式来创建对象了
五、作业
作业一: 场景题目: 有以下几个类, 分别为 人类 学生类 老师类 , 学生类和老师类需要继承人类在人类有以下成员:成员属性: 姓名 年龄 性别 地址 生日成员方法: 吃饭 跑步在学生类: 需要重写吃饭的方法, 学生爱吃麻辣烫属性: 职业: 学生 (固定值)在教师类:需要重写吃饭的方法, 老师爱吃米饭属性: 职业: 教师(固定值)创建一个测试类, 创建三个学生类分别为: 张三 18 男 ...李四 17 女 ...王五 19 男 ...并将三个学生对象, 放置到数组中创建三个教师类: 分别为: 自己写 随意并将三个老师对象, 放置到数组中对学生数组对象, 进行反转操作, 反转后, 通过Arrays将其打印出来对教师数组对象, 采用增强for循环, 遍历打印提示: 在类中,可以重写一个 toString的方法 此方法可以将对象的数据打印, 而不是打印对象的地址值