复习
1.代码块
格式:
{
…
}
局部代码块:
即放在方法中,作用就是控制局部变量的生命周期
构造代码块:
放在在类中,方法外,作用也是给对象进行初始化,优先于构造方法执行。
静态代码块:
放在在类中,方法外,类加载执行,类只加载1次,因此静态块也执行一次。
作用是用于静态变量初始化,加载1次的操作。
2.内部类
概念: 一个类中包含另一个类,里边的类叫做内部类。
根据位置:
(1)成员内部类 : 可以使用成员修饰符进行修饰。
默认修饰符
(2) 私有内部类:
都可以直接访问它所属的外部类中的所有成员
类中只能定义:非静态成员、静态常量
创建对象才可以调用内部类成员。
(3)静态内部类:
类中的成员:静态和非静态
直接访问它所属的外部类中的静态成员。
非静态: 创建内部类对象
静态:内部类名直接访问
(4)局部内部类:
方法中。
类中成员:非静态成员
都可以直接访问它所属的外部类中的所有成员
创建对象才可以调用内部类成员。
3.final关键字
修饰符: 修饰类、方法、变量
类:最终类,不能有子类
方法:最终方法,不能被重写
变量: 最终变量,就是常量,值不能改变。成员,局部。
修饰基本类型变量: 数值不能变
修饰引用类型变量: 引用的指向不能变,即地址值不能变
格式:
权限修饰符 static final 数据类型 常量名 = 数据;
4.包
可看成是电脑磁盘上的文件夹/目录.
作用:分类管理类 源文件和类文件相分离 不同包中可以出现同名的类
package:
package + 包名(公司域名倒置.项目名.模快名)。
类名: 包名.类名
import:
导入的意思,导入指定包下的类,简化类名的编写。
5.权限修饰符
修饰类中成员(变量、构造方法、方法)
private: 当前类
默认:private+同包下
protected:默认+不同包下的子类
public:所有
课程
一. 多态
(一) 多态概述
-
事物的多种表现形态就是多态
java中的多态就理解为对象的不同数据类型的体现也就是子类对象充当父类类型对象 -
多态的发生前提:
(1) 必须要有继承或实现关系
(2) 有方法的重写
(3) 父类引用指向子类的对象 -
格式:
父类类型 变量名 = new 子类名(实参); -
举例 :
设计出一个Person类(表示父类) Teacher类, Person类的子类 Doctor类, Person类的子类 class Person{} class Teacher extends Person{} class Doctor extends Person{}
多态表达式体现:
Person p; // 父类的引用
new Teacher(); // 子类对象
new Doctor(); // 子类对象
Person p = new Teacher();// 人类,可以是一个教师对象,多态表达式
Person p1 = new Doctor();// 人类,可以是一个医生对象,多态表达式
这即是人类的多种表现形态
public class Demo {
public static void main(String[] args) {
/* Person per = new Person(); //本态
Object obj = new Person(); //多态
Object obj1 = new Student(); //多态*/
Person p = new Student() ; //多态
p.eat();
System.out.println(p.num);
}
}
/*
* 多态:
* 是面向对象的第三特征。
* 概念:
* 字面角度: 一个对象有多种形态。
* 实际含义:一个对象可以使用不同类型的引用变量来指向。
* 不同类型的引用变量: 本类型 或 父类型
前提: 必须有继承或实现。
表现形式: 父类类型引用 指向 自己的子类对象
弊端: 调用方法,必须是子父类共有的,子类特有的不能访问
* 访问属性,必须是父类有。
应用:
* 多态参数,方法参数可以是一个父类型的参数,该类型参数可以接受:父类型或其子类型的对象
*
站在引用变量角度看: 可以指向不同的子类对象,间接关系的
站在对象角度看: 可以使用父类型引用来指向,间接关系的
*
*
*
* 多态形式调用方法:
* 编译阶段: 看父类中有没有此方法
* 运行阶段 : 看子类中有没有此方法
* 编译看左,运行看右
*
* 多态形式访问变量:
* 编译和运行看的都是父类。
*/
class Person{
int num = 100;
void eat(){
System.out.println("吃饭");
}
}
class Student extends Person{
int num = 200;
void eat(){
System.out.println("子类----吃饭");
}
}
(二) 多态中成员变量的访问原则
- 原则:
编译看左,运行看左 - 解释:
(1) 编译的时候,要看【=】左边的引用所属的类型中,是否有该变量的定义,如果有,就编译成功,如果没有,就编译失败
(2) 运行的时候,也是使用等号左边类型中的变量
(三) 多态中成员方法的访问特点
- 原则:
编译看左,运行看右 - 解释:
(1) 编译的时候,要看【=】左边的引用所属的类型中,是否有该方法的定义,如果有,就编译成功,如果没有,就编译失败。
(2) 运行的时候,要看【=】右边的对象所属的类型中,是如何实现这个方法的。最终运行的是子类重写过的方法实现。
(四) 向上向下转型
1、向上转型:
使用子类的引用指向子类的对象(正常情况)
多态中,使用父类的引用指向子类的对象(向上转型)
本质:缩小了对象本身的访问范围,减少了访问的权限(只能访问父类中定义的内容)
2、向下转型:
概念:
让指向子类对象的父类引用,【恢复】成子类的引用
格式:
子类类型 引用名称 = (子类类型)父类类型的引用;
本质:
【恢复】子类类型原本就有的访问范围
3、缺陷: 向下转型的时候有可能转为其他子类的类型,编译不会报错但是运行时会发生类型转换异常
4、解决方案:
- 转之前判断一下要转换的类型是不是多态对象之前的类型。使用一个关键字 instanceof 来判断向下转换类型是不是自己的类型
- 格式:
多态对象 instanceof 指定的数据类型 , 返回值为布尔类型的数据
/*
* 转型问题:
* 向上转型: 父类引用指向子类对象 , 多态形式创建的对象。
* 向下转型: 将父类型引用强转为子类型引用
* 注意:在转型的过程中,对象没有变还是子类对象,改变的是对象的引用类型。
*
* instanceof:
* 是一个比较运算符,运算结果是boolean类型
* 作用:判断左边引用是否是指定的类型 ,即判断左右两边的类型是否一致
* 注意:如果是有父子类型关系的判断,先判断子,在判断父
* 如果是兄弟关系的判断,没有顺序之分。
*
* */
public class Demo1 {
public static void main(String[] args) {
printEat(new Cat());
printEat(new Dog());
printEat(new Animal());
/*Animal a = new Animal();
Cat c = (Cat)a; 编译没问题,运行出现类型转换异常 ClassCastException*/
}
//封装方法,根据参数类型的不同,打印对应小动物吃的食物
//调用各自特有的方法
public static void printEat(Animal a){ // Animal a = new Cat(); 向上转型 long = int ; int = (int)long;
if(a instanceof Cat) {
a.eat();
Cat c = (Cat) a; //向下转型
c.mouse();
}else if(a instanceof Dog){
Dog dog = (Dog)a;
dog.eat();
dog.home();
}else{
a.eat();
}
}
/*public static void printEat(Cat cat) { //cat = new Cat();
cat.eat();
}
public static void printEat(Dog dog) {
dog.eat();
}*/
}
class Animal{
void eat(){
System.out.println("吃");
}
}
class Cat extends Animal{
void eat(){
System.out.println("吃鱼");
}
//特有方法
void mouse(){
System.out.println("抓老鼠");
}
}
class Dog extends Animal{
void eat(){
System.out.println("吃骨头");
}
//特有方法
void home(){
System.out.println("拆家");
}
}
4.1 练习
定义人类和超人,分别在人类和超人类中定义名字属性和谈生意功能,并且在超人类中重写人类的谈生意功能,最后在超人类中定义特有的飞翔功能
分析:
- 定义人类,有姓名属性, 谈生意功能
- 定义出一个超人类, 有姓名(可以从人类中继承), 重写谈生意功能, 超人中额外定义出一个特有的飞翔功能
- 定义出一个测试类, 通过多态的方式测试以下超人的各种功能使用
/*
* 定义人类和超人,分别在人类和超人类中定义名字属性和谈生意功能,并且在超人类中重写人类的谈生意功能,最后在超人类中定义
* 特有的飞翔功能
分析:
1.定义人类,有姓名属性, 谈生意功能
2.定义出一个超人类, 有姓名(可以从人类中继承), 重写谈生意功能, 超人中额外定义出一个特有的飞翔功能
3.定义出一个测试类, 通过多态的方式测试以下超人的各种功能使用
* */
public class Demo2 {
public static void main(String[] args) {
Person1 p = new SuperMan();
p.business();
p.name = "超人";
System.out.println(p.name);
//向下转型
SuperMan sup = (SuperMan)p;
sup.fly();
((SuperMan)p).fly();
}
}
class Person1{
String name ;
public void business(){
System.out.println("谈生意");
}
}
class SuperMan extends Person1{
@Override
public void business(){
System.out.println("超人谈生意");
}
public void fly(){
System.out.println("超人会飞");
}
}
(五) 多态的好处
1、提高了代码的扩展性(灵活性)
2、在方法的参数列表中,形式参数是父类类型的引用,将来调用方法的时候,父类类型的任意子类对象,都可以作为方法的实际参数。
3、不在方法的参数列表中,就在普通的方法体中,使用父类的类型指向子类的对象,也能提高代码的可扩展性。对象的来源非常广泛,不仅仅是new出来的,(还可能是通过反射获取的,通过文件读取的,还可能是网络传递的,在写代码的编译阶段,无法知道对象具体的子类类型的)需要使用父类类型的引用,操作不知道的子类类型的对象
案例 : 有一个农场(设计成Farmer类), 农场中可以养很多动物, 农场中有一个方法功能(feed), 可以根据提供动物类型不同, 输出每种动物需要吃的食物
举例 : 给feed方法提供猫类型Cat参数, 方法输出: 猫吃鱼;
给feed方法提供狗类型Dog参数, 方法输出 : 狗啃骨头;
给feed方法提供羊类型Sheep参数, 方法输出 : 羊吃青草;
… 有无数种动物, 提供不同的动物类型, feed方法输出不同动物需要喂食的东西
分析:
- 定义出一个农场类 Farmer, 在这个类中定义出一个方法功能feed
public void feed(){// feed方法的参数列表 : 需要一个动物, 可以是任意一个动物
// 例如 Cat c 或者 Dog d 或者 Sheep s …
// 规律 : 需要的所以的参数, 共性, 属于动物
} - 经过1的分析, 需要定义出一个Animal动物类, 所有的实际动物都应该是Animal类型的子类, 于是将Animal父类类型作为feed方法的形式参数, 当调用feed方法的时候, Animal类型的任意一个子类,都可以作为方法的实际参数
/*
* 描述:
* person、student、teacher
* 属性和方法自行添加,要求student和teacher有自己特有的属性和方法
* 创建一个数组,可以存储person、student、teacher
* 遍历数组,打印存储的对象的属性信息。
* */
public class Demo3 {
public static void main(String[] args) {
//创建数组
Person2[] pers = new Person2[3]; //数组存储的是引用,引用都是Person2类型
pers[0] = new Student1("小红",16,1001); //多态,父类型引用指向子类对象
pers[1] = new Teacher("黎老师",38,20001); //多态,父类型引用指向子类对象
pers[2] = new Person2("张三",20); //本态,自己类型引用指向自己类型对象
//遍历数组
for (int i = 0; i < pers.length; i++){
System.out.println(pers[i].getInfo()); //多态形式的引用,调用方法,编译看父类,运行看子类
}
System.out.println("------------------------------");
for(Person2 p:pers){
System.out.println(p.getInfo());
}
System.out.println("------------------------------");
for(Person2 p : pers){
if(p instanceof Student1){
Student1 stu = (Student1)p;
System.out.println(stu.getName()+"..."+stu.getAge()+"..."+stu.getsId());
}else if(p instanceof Teacher){
Teacher tea = (Teacher)p;
System.out.println(tea.getName()+"..."+tea.getAge()+"..."+tea.gettId());
}else{
System.out.println(p.getName()+"..."+p.getAge());
}
}
}
}
class Teacher extends Person2{
private int tId;
public Teacher() {
}
public Teacher(String name, int age, int tId) {
super(name, age);
this.tId = tId;
}
public int gettId() {
return tId;
}
public void settId(int tId) {
this.tId = tId;
}
@Override
public String getInfo() {
return super.getInfo()+"...."+tId;
}
}
class Student1 extends Person2{
private int sId;
public Student1() {
}
public Student1(String name, int age, int sId) {
super(name, age);
this.sId = sId;
}
public int getsId() {
return sId;
}
public void setsId(int sId) {
this.sId = sId;
}
@Override
public String getInfo() {
return super.getInfo()+"...."+sId;
}
}
class Person2{
private String name;
private int age;
public Person2() {
}
public Person2(String name, int age) {
this.name = name;
this.age = 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 String getInfo(){
return name+"...."+age;
}
}
二. 抽象类
(一) 抽象方法
1、抽象方法:java中只有方法声明没有方法实现并且被关键字abstract修饰的方法就是抽象方法
2、格式:
修饰符 abstract 返回值类型 方法名(参数列表);
3、作用:
提取相同功能不同结果的方法的声明
4、注意:
抽象方法只能定义在抽象类中和接口中
(二) 抽象类
1、抽象类: 可以定义抽象方法的类,就是抽象类。
2、java中被关键字abstract修饰的类就是抽象类
3、定义格式:
修饰符 abstract class 类名 {
}
(三) 抽象类的特点
1、抽象类和抽象方法都需要使用abstract关键字修饰
抽象类:abstract class {}
抽象方法:public abstract void test();
2、抽象类和抽象方法的关系:
抽象方法所在的类必须是抽象类
抽象类中未必一定都定义抽象方法,抽象类中未必存在抽象方法
3、抽象类的实例化
抽象类不能直接实例化
定义抽象类的子类,由子类创建对象,调用方法
注意 : 抽象类存在的意义就是为了有子类继承, 重写抽象方法
4、抽象类子类的前途
在子类中,将父类所有的抽象方法全部重写(实现),子类就成了一个普通类,就可以创建对象
在子类中,没有将父类中所有的抽象方法全部实现,子类就还是一个抽象类,还需要使用abstract关键字修饰子类。
(四) 抽象类中的成员
1、成员变量:可以定义变量,也可以定义常量,但是不能被抽象
2、构造方法:有
虽然抽象类无法创建对象,但是抽象类一定有子类,子类会访问父类的抽象方法。
是否有构造方法,不取决于是否可以创建对象,而是取决于是否可以定义成员变量。如果可以定义成员变量,那么就需要初始化成员变量,就是构造方法来完成的。
3、成员方法:
既可以是抽象方法:强制子类重写
也可以是非抽象方法:用于给子类继承,提高代码的复用性
public class Demo4 {
public static void main(String[] args) {
//new Animal1().eat();
Animal1 a = new Cat1(); //多态
}
}
/*
* 抽象类:
* 使用关键字abstract修饰的类
* abstract:
* 修饰符,修饰类或方法的。
* 修饰类,类就是抽象类;修饰方法:抽象方法
* 特点:
* 1.抽象类中可以有抽象方法,也可以没有抽象方法
* 2.抽象方法要在抽象类中
* 3.抽象类不可以实例化,即不能创建对象
* 4.抽象类通常是作为父类出现的,子类继承抽象父类后,要将继承的抽象方法全部重写,否则子类还是抽象类
* 5.抽象父类型引用指向自己的子类对象---多态
* 抽象类中的成员:
* 变量、方法、构造方法、抽象方法
* */
abstract class Animal1{
int age;
public Animal1(){
}
abstract void eat();
public void method(){
System.out.println("method");
}
}
class Dog1 extends Animal1{
public Dog1(){
super();
}
void eat(){
System.out.println("吃骨头");
}
void home(){
System.out.println("拆家");
}
}
class Cat1 extends Animal1{
void eat(){
System.out.println("吃鱼");
}
void mouse(){
System.out.println("抓老鼠");
}
}
(五) 员工类练习
程序员类:属性(姓名、工号、工资、奖金),行为(工作:软件开发)
测试工程师:属性(姓名、工号、工资),行为(工作:软件测试)
项目经理类:属性(姓名、工号、工资、奖金),行为(工作:控制进度)
使用继承思想实现
分析:
- 上述三个类,都是属于员工, 因此将三个类的共性抽取到父类Employee员工类中
属性: 姓名, 工号, 工资
功能: 工作 - 程序员类, 项目经理类, 特有属性 : 奖金
/*
* 程序员类:属性(姓名、工号、工资、奖金),行为(工作:软件开发)
测试工程师:属性(姓名、工号、工资),行为(工作:软件测试)
项目经理类:属性(姓名、工号、工资、奖金),行为(工作:控制进度)
使用继承思想实现: is-->a
分析: 程序员类--->员工 is-->a
1.上述三个类,都是属于员工, 因此将三个类的共性抽取到父类Employee员工类中
属性: 姓名, 工号, 工资
功能: 工作
2.程序员类, 项目经理类, 特有属性 : 奖金
* */
abstract class Employee{
//属性
private String name;
private int eId;
private double salary;
public Employee(String name, int eId, double salary) {
this.name = name;
this.eId = eId;
this.salary = salary;
}
//方法
public abstract void work();
}
//程序员类
class Programmer extends Employee{
private int bonus; //奖金
public Programmer(String name, int eId, double salary, int bonus) {
super(name, eId, salary);
this.bonus = bonus;
}
@Override
public void work() {
System.out.println("软件开发");
}
}
//测试工程师
class Tester extends Employee{
public Tester(String name, int eId, double salary) {
super(name, eId, salary);
}
@Override
public void work() {
System.out.println("软件测试");
}
}
//项目经理类
class Manager extends Employee{
private int bonus; //奖金
public Manager(String name, int eId, double salary, int bonus) {
super(name, eId, salary);
this.bonus = bonus;
}
@Override
public void work() {
System.out.println("控制进度");
}
}
public class Demo5 {
public static void main(String[] args) {
Employee e1 = new Programmer("张工",1001,10000,50000);
Employee e2 = new Tester("李测",2001,8000);
Employee e3 = new Manager("王经理",3001,2000,10000);
e1.work();
e2.work();
e3.work();
}
}
三. 接口
(一) 接口的概述
- 接口是java用来描述多种不同规则的集合体;
- 规则在java中就是抽象方法,接口就是存放了不同的抽象方法的集合体
好处:
一旦将命名规则定义出来,【方法的调用】和【方法的实现】就分离开了,可以提升开发效率,降低代码的耦合性 - java中使用关键字interface表示,接口和类同级别的
接口的源文件也是.java文件,照样参与编译,编译后的文件依然是字节码文件【.class】
(二) 接口的定义特点及注意事项
-
格式:
修饰符 interface 接口名 {接口的内容} -
内容:
属性:接口中的成员变量, 实际是成员常量,默认被 public static final修饰(使用接口名访问即可)
方法:JDK1.8之前, 只有抽象方法, 默认被public abstract 修饰 -
注意事项:
(1) 没有构造方法,不能直接创建对象
(2) 抽象方法必须需要类来实现,之后被类的对象调用使用
(三) 接口实现
-
接口书写好之后里面的规则要想被使用,需要类来重写,使类拥有接口中的规则功能,如此类和接口就发生关联关系, 类去重写接口规则的过程就叫做实现接口
-
类实现接口:
使用关键字implements连接类和接口 -
格式:
类 implements 接口名1,接口名2....{类的内容}
-
接口的实现类前途:
是一个抽象类,该类没有实现接口中的所有抽象方法
是一个普通类,该类实现了接口中的所有抽象方法 -
单实现:
一个类实现了一个接口的实现方式类 implements 接口名{ 重写接口中所有的抽象方法 }
-
多实现:
一个类同时实现多个不同接口的实现方式就是多实现类 implements 接口名1,接口名2....{ 重写所有接口中所有的抽象方法 不同的接口中相同方法声明的抽象方法只需要重写一次 }
(四) 类与类、类与接口、接口与接口之间的关系
1、类与类
继承的关系,使用extends关键字
可以单继承、不可以多继承、可以多层继承
2、类与接口:
实现关系,使用implements关键字
java中有单实现和多实现
多实现没有安全隐患:即使两个接口中有一样的方法声明,但是在类中也只有一个实现
3、在继承一个父类的前提下,还可以实现多个接口
格式:
class 子类类名 extends 父类类名 implements 接口1, 接口2.....{
//重写父类和所有接口中的所有抽象方法
}
注意 : 父优先
4、接口与接口:
继承关系,使用extends
可以单继承、也可以多继承、可以多层继承
多继承的格式:
interface 接口名 extends 父接口1, 父接口2.....{
相当于继承了所有父接口的所有抽象方法
}
5、类和接口的区别(设计区别):
抽象类:定义事物本身具有的固有属性和行为
接口:定义事物通过学习、训练而扩展出来的行为
public class Demo6 {
public static void main(String[] args) {
//new Inter();
MyInter my = new MyInter(); //本态
Inter in = new MyInter(); //多态
A a = new MyInter();
a.show();
B b = new MyInter();
b.show();
//常量
System.out.println(Inter.NUM);
System.out.println(MyInter.NUM);
System.out.println(my.NUM);
}
}
//定义接口
interface Inter{
public static final int NUM= 100;
public abstract void method();
}
interface A{
void show();
}
interface B{
void show();
}
class MyInter implements Inter,A,B{
@Override
public void method() {
System.out.println("method");
}
@Override
public void show() {
System.out.println("show");
}
}
/*
* 引用类型:
* 数组 类 接口 枚举 注解
*
* 接口:
* 用来定义不同规则的集合。
* 不同规则:方法的声明
* 格式:
* 修饰符 interface 接口名{
* 常量;...
* 抽象方法;...
* }
* 接口和类是同级别的,因此编译后也是class文件。
* 接口中的成员都是公共的,修饰符都是固定的,因此可以不用写完整,编译器会自动补全。建议写完整。
*
* 特点:
* 1.接口不能创建对象
* 2.要有实现类(子类)实现接口,implements(实现),子类要将所有的抽象方法全部实现,否则子类还是抽象类。
* 3父类型接口引用可以指向自己的实现类对象.
* 4.一个实现类可以实现多个接口,接口名之间使用逗号分隔,最后一个没有逗号
* */