面向对象进阶
Java学习笔记(新手纯小白向)
第一章 JAVA基础概念
第二章 JAVA安装和环境配置
第三章 IntelliJ IDEA安装
第四章 运算符
第五章 运算符联系
第六章 判断与循环
第七章 判断与循环练习
第八章 循环高级综合
第九章 数组介绍及其内存图
第十章 数组基础练习
第十一章 方法基础及简单应用
第十二章 方法基础练习
第十三章 前续知识综合练习
第十四章 面向对象基础
第十五章 面向对象综合训练
第十六章 字符串基础
第十七章 字符串基础练习
第十八章 ArrayList集合
第十九章 ArrayList集合基础练习
第二十章 面向对象进阶
目录
前言
本篇章主要介绍了面向对象的一些较深入的概念及其使用方法与应用场景
一、static与工具类
1. 类
1、JavaBean类:用来描述一类事物的类。比如,Student、Teacher、Dog、Cat等
2、测试类:用来检查其他类是否书写正确,带有main方法的类,是程序的入口
3、工具类:不是用来描述一类事物的,而是帮我们做一些事情的类
2.工具类的定义
1、类名见名知意
2、私有化构造方法
3、方法定义为静态
4、代码演示
public class Student {
//定义学生的属性
private String name;
private int age;
private String gender;
//定义空参构造和带参构造
public Student() {
}
public Student(String name, int age, String gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
//定义成员变量的get和set方法
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 getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
}
public class StudentUtil {
//私有化构造方法
private StudentUtil() {
}
//定义静态方法getMax,获取集合中学生的最大年龄
public static int getMax(ArrayList<Student> students) {
//遍历集合,比较学生的年龄
//定义变量max,记录学生的最大年龄
int max = students.get(0).getAge();
for (int i = 1; i < students.size(); i++) {
//定义变量age,记录学生的年龄
int tempAge = students.get(i).getAge();
if (tempAge > max) {
max = tempAge;
}
}
return max;
}
}
public class StudentTest {
public static void main(String[] args) {
//定义集合students,存储学生对象
ArrayList<Student> students = new ArrayList<>();
//创建学生对象,并将其存入集合
Student student1 = new Student("zhangsan", 18, "man");
Student student2 = new Student("lisi", 19, "man");
Student student3 = new Student("wangwu", 20, "woman");
students.add(student1);
students.add(student2);
students.add(student3);
//调用方法getMax,获取集合中学生的最大年龄
int maxAge = StudentUtil.getMax(students);
System.out.println("最大年龄为:" + maxAge);
}
}
3.static概述
1、静态变量
被static修饰的成员变量,叫做静态变量
(1)特点:
1)被该类所有对象共享
2)跟对象无关,随着类的加载而加载,优先于对象存在
(2)调用方式:
1)类名调用(推荐)
2)对象名调用
(3)代码演示
public class Student {
//定义学生的属性
private String name;
private int age;
private String gender;
//新增属性,老师
public static String teacherName;
//定义空参构造和带参构造
public Student() {
}
public Student(String name, int age, String gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
//定义get和set方法
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 getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
//定义方法study,代表行为:学习
public void study() {
System.out.println(name + "正在学习");
}
//定义方法,展示对象的属性信息
public void show() {
System.out.println(name + "," + age + "," + gender + "," + teacherName);
}
}
public class StudentTest {
public static void main(String[] args) {
//创建第一个学生对象
Student s1 = new Student();
s1.setName("zhangsan");
s1.setAge(23);
s1.setGender("男");
s1.teacherName = "wangwu";
//调用方法study和show
s1.study();
s1.show();
//创建第二个学生对象
Student s2 = new Student();
s2.setName("lisi");
s2.setAge(24);
s2.setGender("女");
//调用方法study和show
s2.study();
s2.show();
}
}
2、静态方法
被static修饰的成员方法,叫做静态方法
(1)特点:
1)多用在测试类和工具类中
2)JavaBean类中很少会用
(2)调用方式:
1)类名调用(推荐)
2)对象名调用
(3)代码演示
public class ArrayUtil {
//私有化构造方法
private ArrayUtil() {
}
//定义方法printArr,用于返回整数数组的内容
public static String printArr(int[] array) {
//创建StringBuilder对象sb
StringBuilder sb = new StringBuilder("[");
//遍历数组,将数组中的数据加入容器
for (int i = 0; i < array.length; i++) {
if (i == array.length - 1) {
sb.append(array[i]).append("]");
} else {
sb.append(array[i]).append(",");
}
}
return sb.toString();
}
//定义方法getAverage,用于返回平均数
public static double getAverage(double[] array) {
//定义变量sum,存储总数
double sum = 0;
//遍历数组
for (int i = 0; i < array.length; i++) {
sum = sum + array[i];
}
//返回平均数
return sum / array.length;
}
}
public class TestDemo {
public static void main(String[] args) {
//定义静态数组
int[] array1 = {10, 20, 50, 34, 100};
double[] array2 = {89.7, 79.8};
//调用方法,返回数组内容
System.out.println(ArrayUtil.printArr(array1));
//调用方法,返回平均数
System.out.println("平均数为:" + ArrayUtil.getAverage(array2));
}
}
3、static的注意事项
(1)静态方法只能访问静态变量和静态方法
(2)非静态方法可以访问静态变量或者静态方法,也可以访问非静态的成员变量和非静态的成员方法
(3)静态方法中没有this关键字
4、static内存图
(1)静态变量是随着类的加载而加载的,优先于对象出现的
(2)静态方法不能访问非静态
(3)静态方法不调用实例变量
5、重新认识main方法
public class demo04 {
public static void main(String[] args) {
//String:数据类型
//[]:数组
//args:数组名
System.out.println(args.length);
for (int i = 0; i < args.length; i++) {
System.out.println(args[i]);
}
}
}
(1)public:被JVM调用,访问权限足够大
(2)static:被JVM调用,不用创建对象,直接类名访问。因为main方法是静态的,所以测试类中其他方法也需要是静态的
(3)void:被JVM调用,不需要给JVM返回值
(4)main:一个通用的名称,虽然不是关键字,但是被JVM识别
(5)String[ ] args:以前用于接受键盘录入数据,现在没用
二、继承
1.继承概念及其好处
1、概念
继承是面向对象的三大特征之一,可以让类跟类之间产生父子关系。
2、好处
可以把多个子类中重复的代码抽取到父类中,子类可以直接使用,减少代码冗余,提高代码的复用性。
2.继承的格式
public class 子类 extends 父类 {}
1、Java中提供一个关键字extends,用这个关键字,我们可以让一个类和另一个类建立起继承关系。
2、子类又称派生类,父类又称基类或超类。
3.使用继承的好处
1、可以把多个子类中重复的代码抽取到父类中,提高代码的复用性。
2、子类可以在父类的基础上,增加其他的功能,使子类更加强大
4.继承后子类的特点
1、子类可以得到父类的属性和行为,子类可以直接使用
2、子类可以在父类的基础上新增其他功能,子类更加强大
5.继承的特点
Java只支持单继承,不支持多继承,但支持多层继承
1、多层继承:子类A继承父类B(直接父类),父类B可以继承父类C(间接父类)
2、每一个类都直接或间接的继承于Object
6.子类到底能继承父类中的哪些内容?
1、构造方法:非私有,不能;private,不能
2、成员变量:非私有,能;private,能
3、成员方法:虚方法表,能;否则,不能
4、代码演示:
public class Animal {
//定义成员方法eat,代表动作吃饭
public void eat() {
System.out.println("吃饭");
}
//定义成员方法drink,代表动作喝水
public void drink() {
System.out.println("喝水");
}
}
public class Cat extends Animal {
//定义方法catchingMice,代表动作抓老鼠
public void catchingMice() {
System.out.println("抓老鼠");
}
}
public class Dog extends Animal {
//定义方法houseKeeper,代表动作看家
public void houseKeeper() {
System.out.println("看家");
}
}
public class Dragonli extends Cat {
}
public class Husky extends Dog {
//定义成员方法destroyHome,代表行为拆家
public void destroyHome() {
System.out.println("拆家");
}
}
public class Ragdoll extends Cat{
}
public class Teddy extends Dog {
//定义方法rub,代表行为蹭一蹭
public void rub() {
System.out.println("蹭一蹭 ");
}
}
public class Test {
public static void main(String[] args) {
//创建对象并调用方法
//创建布偶猫的对象
Ragdoll rd = new Ragdoll();
rd.eat();
rd.drink();
rd.catchingMice();
System.out.println("————————————————");
//创建哈士奇的对象
Husky h = new Husky();
h.eat();
h.drink();
h.houseKeeper();
h.destroyHome();
}
}
7.继承中成员变量的访问
1、 特点:就近原则
先在局部位置找,本类成员位置找,父类成员位置找,逐级往上
2、如果出现了重名的成员变量怎么办?
System.out.println(name);
System.out.println(this.name);
System.out.println(super.name);
从局部位置开始往上找,从本类成员位置开始往上找,从父类成员位置开始往上找
8.继承中成员方法的访问特点
1、直接调用满足就近原则:谁离我近,我就用谁
2、super调用,直接访问父类
3、代码演示:
public class Fu {
String name = "Fu";
String hobby = "喝茶";
}
public class Zi extends Fu {
String name = "Zi";
String hobby = "吃鸡";
public void ziShow() {
System.out.println(this.name);
System.out.println(super.name);
System.out.println(super.hobby);
System.out.println(this.hobby);
}
}
public class Test {
public static void main(String[] args) {
Zi z = new Zi();
z.ziShow();
}
}
9.方法重写
1、方法的重写:当父类的方法不能满足子类现在的需求时,需要进行方法重写
2、书写格式:在继承体系中,子类出现了和父类中一摸一样的方法声明,我们就称子类这个方法是重写的方法
3、@Override重写注解
(1)@Override是放在重写后的方法上,校验子类重写时语法是否正确
(2)加上注解后如果有红色波浪线,表示语法错误
(3)建议重写方法都加@Override注解,代码安全,优雅
4、注意事项和要求
(1)重写方法的名称、形参列表必须与父类中的一致
(2)子类重写父类方法时,访问权限必须大于等于父类(暂时了解:空着不写<protected<public)
(3)子类重写父类方法时,返回值类型子类必须小于等于父类
(4)建议:重写的方法尽量和父类保持一致
(5)只有被添加到虚方法表中的方法才能被重写
5、方法重写的本质
覆盖虚方法表中的方法
6、代码演示
public class Dog {
//定义成员方法eat,代表行为:吃饭
public void eat(){
System.out.println("吃狗粮");
}
//定义成员方法drink,代表行为:喝水
public void drink(){
System.out.println("喝水");
}
//定义成员方法lookHome,代表行为:看家
public void lookHome(){
System.out.println("看家");
}
}
public class FieldDog extends Dog {
//重写方法eat
@Override
public void eat() {
System.out.println("吃剩饭");
}
}
public class Husky extends Dog {
//定义方法destoryHome,代表行为:拆家
public void destoryHome() {
System.out.println("拆家");
}
}
public class ShaPi extends Dog {
//重写方法eat
@Override
public void eat() {
super.eat();
System.out.println("吃骨头");
}
}
public class Tset {
public static void main(String[] args) {
//创建Husky对象
Husky hs = new Husky();
//调用方法
hs.eat();
hs.destoryHome();
System.out.println("————————————————");
//创建ShaPi对象
ShaPi sp = new ShaPi();
//调用方法
sp.eat();
System.out.println("————————————————");
//创建FieldDog对象
FieldDog fg = new FieldDog();
//调用方法
fg.eat();
}
}
10.继承中构造方法的访问特点
1、父类中的构造方法不会被子类继承
2、子类中的所有构造方法默认先访问父类中的无参构造,在执行自己
3、为什么?
(1)子类在初始化的时候,有可能会使用到父类中的数据,如果父类没有完成初始化,子类将无法使用父类的数据
(2)子类初始化之前,一定要调用父类构造方法先完成父类数据空间的初始化
4、怎么调用父类构造方法
(1)子类构造方法的第一行语句默认都是super(),不写也存在,且必须在第一行
(2)如果想调用父类有参构造,必须手动写super进行调用
5、代码演示:
public class Peson {
//定义父类的属性
String name;
int age;
//定义空参构造和带参构造
public Peson() {
System.out.println("父类的无参构造");
}
public Peson(String name, int age) {
this.name = name;
this.age = age;
}
}
public class Student extends Peson {
//定义空参构造和带参构造
public Student() {
super();
System.out.println("子类的无参构造");
}
public Student(String name, int age) {
super(name, age);
}
}
public class PersonTest {
public static void main(String[] args) {
//创建学生对象
Student s1 = new Student();
Student s2 = new Student("zhangsan", 23);
System.out.println(s2.name + "," + s2.age);
}
}
11.this、super使用总结
1、this:理解为一个变量,表示当前方法调用者的地址值
2、super:代表父类的存储空间
3、二者区别:
(1) 访问成员变量:
1)this.成员变量,访问本类成员变量
2)super.成员变量,访问父类成员变量
(2)访问成员方法:
1)this.成员方法(...),访问本类成员方法
2)super.成员方法(...),访问父类成员方法
(3)访问构造方法:
1)this(...),访问本类构造方法
2)super(...),访问父类构造方法
4、代码演示:
public class Employee {
//定义雇工的属性
private String id;
private String name;
private double salary;
//定义空参构造和带参构造
public Employee() {
}
public Employee(String id, String name, double salary) {
this.id = id;
this.name = name;
this.salary = salary;
}
//定义成员变量的get和set方法
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
//定义方法work,代表行为:工作
public void work() {
System.out.println("工作");
}
//定义方法eat,代表行为:吃饭
public void eat() {
System.out.println("吃米饭");
}
}
public class Manager extends Employee {
//定义经理的属性
private double manageBonus;
//定义空参构造和带参构造
public Manager() {
super();
}
public Manager(String id, String name, double salary, double manageBonus) {
super(id, name, salary);
this.manageBonus = manageBonus;
}
//定义成员变量的get和set方法
public double getManageBonus() {
return manageBonus;
}
public void setManageBonus(double manageBonus) {
this.manageBonus = manageBonus;
}
//重写work方法
@Override
public void work() {
System.out.println("管理其他人");
}
}
public class Cook extends Employee {
//定义空参构造和带参构造
public Cook() {
super();
}
public Cook(String id, String name, double salary) {
super(id, name, salary);
}
//重写work方法
@Override
public void work() {
System.out.println("炒菜");
}
}
public class EmployeeTest {
public static void main(String[] args) {
//创建对象
Manager m = new Manager("001", "zhangsan", 15000, 8000);
Cook c = new Cook();
c.setId("002");
c.setName("lisi");
c.setSalary(99);
//打印信息,并调用方法
System.out.println(m.getId() + "," + m.getName() + "," + m.getSalary() + "," + m.getManageBonus());
m.eat();
m.work();
System.out.println("——————————————");
System.out.println(c.getId() + "," + c.getName() + "," + c.getSalary());
c.eat();
c.work();
}
}
三、多态
1.多态的基础概念
1、什么是多态?
同类型的对象,表现出的多种形态,即对象的多种形态。
2、多态的表现形式
父类类型 变量名 = new 子类/实现类构造器;
变量名.方法名();
3、多态的前提
(1)有继承/实现关系。子类对象是可以赋值给父类类型的变量。例如Animal是一个动物类型,而Cat是一个猫类型。Cat继承了Animal,Cat对象也是Animal类型,自然可以赋值给父类类型的变量。
(2)有父类引用指向子类对象。
(3)有方法重写。
4、多态的好处
(1)使用父类型作为参数,可以接受所有的子类对象
(2)体现多态的扩展性与便利
5、代码示例
public class Person {
//定义人的属性
private String name;
private int age;
//定义空参构造和带参构造
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
//定义get和set方法
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;
}
//定义方法show,打印信息
public void show() {
System.out.println(name + "," + age);
}
}
public class Administrator extends Person {
//重写show方法
@Override
public void show() {
System.out.println("管理员的信息为" + getName() + "," + getAge());
}
}
public class Student extends Person {
//重写show方法
@Override
public void show() {
System.out.println("学生的信息为" + getName() + "," + getAge());
}
}
public class Teacher extends Person {
//重写show方法
@Override
public void show() {
System.out.println("老师的信息为" + getName() + "," + getAge());
}
}
public class Test {
public static void main(String[] args) {
//创建三个对象,并调用register方法
Student s = new Student();
s.setName("张三");
s.setAge(18);
Teacher t = new Teacher();
t.setName("王建国");
t.setAge(30);
Administrator admin = new Administrator();
admin.setName("管理员");
admin.setAge(35);
register(s);
register(t);
register(admin);
}
//定义方法register,代表行为:注册
//这个方法既能接收老师,又能接收学生,还能接收管理员
//只能把参数写成这三个类型的父类
public static void register(Person p) {
p.show();
}
}
2.多态中调用成员的特点
1、调用成员变量的特点
编译看左边,运行也看左边
(1)编译看左边:Javac编译代码的时候,会看左边的父类中有没有这个变量,如果有,编译成功,如果没有编译失败。
(2)运行也看左边:Java运行代码的时候,实际获取的就是左边父类中成员变量的值。
2、调用成员方法的特点
编译看左边,运行看右边
(1)编译看左边:Javac编译代码的时候,会看左边的父类中有没有这个方法,如果有,编译成功,如果没有编译失败。
(2)运行看右边:Java运行代码的时候,实际上运行的是子类中的方法
3、代码示例
Fu f = new Zi();
//编译看左边的父类中有没有name这个属性,没有就报错
//在实际运行的时候,把父类name属性的值打印出来
System.out.println(f.name);
//编译看左边的父类中有没有show这个方法,没有就报错
//在实际运行的时候,运行的是子类中的show方法
f.show();
3.多态的优势和弊端
1、优势
(1)在多态形式下,右边对象可以实现解耦合,便于扩展和维护。
Person p = new Student();
p.work(); //业务逻辑发生改变时,后续代码无需修改
(2)定义方法的时候,使用父类型作为参数,可以接收所有子类对象,体现多态的扩展性与便利。
2、弊端
(1)不能调用子类的特有功能。
(2)报错原因:当调用成员方法时,编译看左边,运行看右边。那么在编译的时候会先检查左边的父类中有没有这个方法,如果没有直接报错。
(3)解决方案:变回子类类型。
3、代码示例
class Animal{
public void eat(){
System.out.println("动物吃东西!")
}
}
class Cat extends Animal {
public void eat() {
System.out.println("吃鱼");
}
public void catchMouse() {
System.out.println("抓老鼠");
}
}
class Dog extends Animal {
public void eat() {
System.out.println("吃骨头");
}
}
class Test{
public static void main(String[] args){
Animal a = new Cat();
a.eat();
a.catchMouse();//编译报错,编译看左边,Animal没有这个方法
}
}
4.引用数据类型的类型转换
1、转换方式
(1)自动转换: 范围小的赋值给范围大的,自动完成:double d = 5
(2)强制转换: 范围大的赋值给范围小的,强制转换:int i = (int)3.14
2、多态的转型
(1)向上转型(自动转换)
多态本身是子类类型向父类类型向上转换(自动转换)的过程,这个过程是默认的。 当父类引用指向一个子类对象时,便是向上转型。 使用格式:
父类类型 变量名 = new 子类类型();
如:Animal a = new Cat();
原因是:父类类型相对与子类来说是大范围的类型,Animal是动物类,是父类类型。Cat是猫类,是子类类型。Animal类型的范围当然很大,包含一切动物。所以子类范围小可以直接自动转型给父类类型的变量。
(2)向下转型(强制转换)
父类类型向子类类型向下转换的过程,这个过程是强制的。 一个已经向上转型的子类对象,将父类引用转为子类引用,可以使用强制类型转换的格式,便是向下转型。使用格式:
子类类型 变量名 = (子类类型) 父类变量名;
如:Aniaml a = new Cat();
Cat c =(Cat) a;
(3)转型异常
转型的过程中,一不小心就会遇到这样的问题,请看如下代码:
public class Test {
public static void main(String[] args) {
// 向上转型
Animal a = new Cat();
a.eat(); // 调用的是 Cat 的 eat
// 向下转型
Dog d = (Dog)a;
d.watchHouse(); // 调用的是 Dog 的 watchHouse 【运行报错】
}
}
这段代码可以通过编译,但是运行时,却报出了 ClassCastException
,类型转换异常!这是因为,明明创建了Cat类型对象,运行时,当然不能转换成Dog对象的。
(4)instanceof关键字
为了避免ClassCastException的发生,Java提供了 instanceof
关键字,给引用变量做类型的校验,格式如下:
变量名 instanceof 数据类型
如果变量属于该数据类型或者其子类类型,返回true。
如果变量不属于该数据类型或者其子类类型,返回false。
所以,转换前,我们最好先做一个判断,代码如下:
public class Test {
public static void main(String[] args) {
// 向上转型
Animal a = new Cat();
a.eat(); // 调用的是 Cat 的 eat
// 向下转型
if (a instanceof Cat){
Cat c = (Cat)a;
c.catchMouse(); // 调用的是 Cat 的 catchMouse
} else if (a instanceof Dog){
Dog d = (Dog)a;
d.watchHouse(); // 调用的是 Dog 的 watchHouse
}
}
}
(5)instanceof新特性
JDK14的时候提出了新特性,把判断和强转合并成了一行
//新特性
//先判断a是否为Dog类型,如果是,则强转成Dog类型,转换之后变量名为d
//如果不是,则不强转,结果直接是false
if(a instanceof Dog d){
d.lookHome();
}else if(a instanceof Cat c){
c.catchMouse();
}else{
System.out.println("没有这个类型,无法转换");
}
3、强制类型转换解决的问题
(1)可以转换成真正的子类类型,从而调用子类独有功能。
(2)转换类型与真实对象类型不一致会报错。
(3)转换的时候用instanceof关键字进行判断。
四、综合练习
1、需求
需求:根据需求完成代码:
1.定义狗类
属性:
年龄,颜色
行为:
eat(String something)(something表示吃的东西)
看家lookHome方法(无参数)
2.定义猫类
属性:
年龄,颜色
行为:
eat(String something)方法(something表示吃的东西)
逮老鼠catchMouse方法(无参数)
3.定义Person类//饲养员
属性:
姓名,年龄
行为:
keepPet(Dog dog,String something)方法
功能:喂养宠物狗,something表示喂养的东西
行为:
keepPet(Cat cat,String something)方法
功能:喂养宠物猫,something表示喂养的东西
生成空参有参构造,set和get方法
4.定义测试类(完成以下打印效果):
keepPet(Dog dog,String somethind)方法打印内容如下:
年龄为30岁的老王养了一只黑颜色的2岁的狗
2岁的黑颜色的狗两只前腿死死的抱住骨头猛吃
keepPet(Cat cat,String somethind)方法打印内容如下:
年龄为25岁的老李养了一只灰颜色的3岁的猫
3岁的灰颜色的猫眯着眼睛侧着头吃鱼
5.思考:
1.Dog和Cat都是Animal的子类,以上案例中针对不同的动物,定义了不同的keepPet方法,过于繁琐,能否简化,并体会简化后的好处?
2.Dog和Cat虽然都是Animal的子类,但是都有其特有方法,能否想办法在keepPet中调用特有方法?
2、画图分析
3、代码示例
public class Animal {
//定义动物的属性
private int age;
private String color;
private String variety;
//定义空参构造和带参构造
public Animal() {
}
public Animal(int age, String color, String variety) {
this.age = age;
this.color = color;
this.variety = variety;
}
//定义成员变量的get和set方法
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public String getVariety() {
return variety;
}
public void setVariety(String variety) {
this.variety = variety;
}
//定义方法eat,代表行为:吃饭
public void eat(String sth) {
System.out.println("在吃" + sth);
}
}
public class Cat extends Animal {
//定义空参构造和带参构造
public Cat() {
}
public Cat(int age, String color, String variety) {
super(age, color, variety);
}
//重写方法eat
@Override
public void eat(String sth) {
System.out.println(getAge() + "岁的" + getColor() + "的" + getVariety()
+ "眯着眼睛侧着头吃" + sth);
}
//定义方法catchMouse,代表行为:逮老鼠
public void catchMouse() {
System.out.println("猫在逮老鼠");
}
}
public class Dog extends Animal {
//定义空参构造和带参构造
public Dog() {
}
public Dog(int age, String color, String variety) {
super(age, color, variety);
}
//重写方法eat
@Override
public void eat(String sth) {
System.out.println(getAge() + "岁的" + getColor() + "的" + getVariety()
+ "两只前腿死死的抱住" + sth + "猛吃");
}
//定义方法lookHome,代表行为:看家
public void lookHome() {
System.out.println("狗在看家");
}
}
public class Person {
//定义饲养员的属性
private String name;
private int age;
//定义空参构造和带参构造
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
//定义成员变量的get和set方法
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;
}
//定义方法keepPet,代表行为:喂养宠物
public void keepPet(Animal animal, String sth) {
System.out.println("年龄为" + age + "岁的" + name + "养了一只"
+ animal.getColor() + "的" + animal.getAge() + "岁的" + animal.getVariety());
animal.eat(sth);
if (animal instanceof Dog dog) {
/*System.out.println("年龄为" + age + "岁的" + name + "养了一只"
+ dog.getColor() + "的" + dog.getAge() + "岁的" + dog.getVariety());
dog.eat(sth);*/
dog.lookHome();
} else if (animal instanceof Cat cat) {
/*System.out.println("年龄为" + age + "岁的" + name + "养了一只"
+ cat.getColor() + "的" + cat.getAge() + "岁的" + cat.getVariety());
cat.eat(sth);*/
cat.catchMouse();
} else {
System.out.println("没有这种动物");
}
}
}
public class Test {
//根据需求完成代码:
//1.定义狗类
// 属性:
// 年龄,颜色
// 行为:
// eat(String something)(something表示吃的东西)
// 看家lookHome方法(无参数)
//
//2.定义猫类
// 属性:
// 年龄,颜色
// 行为:
// eat(String something)方法(something表示吃的东西)
// 逮老鼠catchMouse方法(无参数)
//
//3.定义Person类//饲养员
// 属性:
// 姓名,年龄
// 行为:
// keepPet(Dog dog,String something)方法
// 功能:喂养宠物狗,something表示喂养的东西
// 行为:
// keepPet(Cat cat,String something)方法
// 功能:喂养宠物猫,something表示喂养的东西
// 生成空参有参构造,set和get方法
//4.定义测试类(完成以下打印效果):
// keepPet(Dog dog,String somethind)方法打印内容如下:
// 年龄为30岁的老王养了一只黑颜色的2岁的狗
// 2岁的黑颜色的狗两只前腿死死的抱住骨头猛吃
// keepPet(Cat cat,String somethind)方法打印内容如下:
// 年龄为25岁的老李养了一只灰颜色的3岁的猫
// 3岁的灰颜色的猫眯着眼睛侧着头吃鱼
// 5.思考:
// 1.Dog和Cat都是Animal的子类,以上案例中针对不同的动物,定义了不同的keepPet方法,过于繁琐,能否简化,并体会简化后的好处?
// 2.Dog和Cat虽然都是Animal的子类,但是都有其特有方法,能否想办法在keepPet中调用特有方法?
public static void main(String[] args) {
//创建对象
Animal dog = new Dog(2, "黑颜色", "狗");
Animal cat = new Cat(3, "灰颜色", "猫");
Person person1 = new Person("老王", 30);
Person person2 = new Person("老李", 25);
//调用方法keepPet方法
person1.keepPet(dog, "骨头");
person2.keepPet(cat, "鱼");
}
}
五、包和final
1、什么是包?
包就是文件夹。用来管理各种不同功能的Java类,方便后期代码维护。
(1)包名的规则
公司域名反写+包的作用,需要全部英文小写,见名知意。com.itheima.domain
(2)全类名
包名+类名
(3)使用其它类的规则(导包)
1)使用同一个包的类时,不需要导包。
2)使用java.lang包中的类时,不需要导包。
3)其他情况都需要导包。
4)如果同时使用两个包中的同名类,需要用全类名。
2、final
(1)final修饰方法:表明该方法是最终方法,不能被重写。
(2)final修饰类:表明该类是最终类,不能被继承。
(3)final修饰变量:叫做常量,只能被赋值一次。
(4)常量
1)实际开发中,常量一般作为系统的配置信息,方便维护,提高可读性。
2)命名规范
单个单词:全部大写
多个单词:全部大写,单词之间用下划线隔开
3)细节
final修饰的变量是基本数据类型:那么变量存储的数据值不能发生改变。
final修饰的变量是引用数据类型:那么变量存储的地址值不能发生改变,对象内部的属性值可以改变。
4)代码示例
public class Test {
}
final class Fu {
public final void show() {
System.out.println("父类的show方法");
}
}
/*class Zi extends Fu {
@Override
public void show() {
System.out.println("子类的show方法");
}
}*/
六、权限修饰符和代码块
1、权限修饰符
权限修饰符是用来控制一个成员能够被访问的范围的。可以修饰成员变量、方法、构造方法、内部类。
(1)分类
public:公共的,所有地方都可以访问。
protected:本类 ,本包,其他包中的子类都可以访问。
默认(没有修饰符):本类 ,本包可以访问。
注意:默认是空着不写,不是default
private:私有的,当前类可以访问。 public > protected > 默认 > private
(2)不同权限的访问能力
public | protected | 默认 | private | |
---|---|---|---|---|
同一类中 | √ | √ | √ | √ |
同一包中的类 | √ | √ | √ | |
不同包的子类 | √ | √ | ||
不同包中的无关类 | √ |
可见,public具有最大权限。private则是最小权限。
编写代码时,如果没有特殊的考虑,建议这样使用权限:
- 成员变量使用
private
,隐藏细节。 - 构造方法使用
public
,方便创建对象。 - 成员方法使用
public
,方便调用方法。
(3)使用规则
1)实际开发中,一般只用private和public
成员变量私有
方法公开
2)特例
如果方法中的代码是抽取其他方法中共性代码,这个方法一般也私有。
2、代码块
1.局部代码块
(1)作用:提前结束变量的生命周期(已淘汰)
(2)代码示例
public class Test {
public static void main(String[] args) {
{
int a = 10;
System.out.println(a);
}
}
}
2.构造代码块
写在成员位置的代码块
(1)作用:抽取构造方法中的重复代码(不够灵活)
(2)执行时机:在创建本类对象的时候会先执行构造代码块再执行构造方法
(3)代码示例
public class Student {
private String name;
private int age;
{
System.out.println("开始创建对象了");
}
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
}
3.静态代码块
(1)格式:static{}
(2)特点:需要通过static关键字修饰,随着类的加载而加载,并且自动触发、只执行一次。
(3)使用场景:在类加载的时候,做一些数据初始化的时候使用
七、抽象类和抽象方法
1、抽象方法
1.概念
将共性的行为(方法)抽取到父类之后。由于每一个子类执行的内容是不一样的,所以,在父类中不能确定具体的方法体。该方法就可以定义为抽象方法。
2.定义格式
public abstract 返回值类型 方法名(参数列表);
2、抽象类
1.概念
如果一个类中存在抽象方法,那么该类就必须沈明为抽象类。
2.定义格式
public abstract class 类名{}
3.作用
抽取共性时,无法确定方法体,就把方法定义为抽象的。强制让子类按照某种格式重写。抽象方法所在的类,必须是抽象类。
3、注意事项
1.抽象类不能实例化
2.抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
3.可以有构造方法
4.抽象类的子类
(1)要么重写抽象类中的所有抽象方法
(2)要么是抽象类
4、代码示例
public abstract class Animal {
//定义动物的属性
private String name;
private int age;
//定义空参构造和带参构造
public Animal() {
}
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
//定义成员变量的get和set方法
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;
}
//定义抽象方法eat,代表行为:吃东西
public abstract void eat();
//定义方法drink,代表行为:喝水
public void drink() {
System.out.println("喝水");
}
}
public class Dog extends Animal {
//定义空参构造和带参构造
public Dog() {
}
public Dog(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println("吃骨头");
}
}
public class Frog extends Animal {
//定义空参构造和带参构造
public Frog() {
}
public Frog(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println("吃虫子");
}
}
public class Sheep extends Animal {
//定义空参构造和带参构造
public Sheep() {
}
public Sheep(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println("吃草");
}
}
public class Test {
public static void main(String[] args) {
//创建对象
Frog frog = new Frog("wangzi", 1);
Dog dog = new Dog("haba", 2);
Sheep sheep = new Sheep("xiaoen", 3);
//调用方法
frog.eat();
dog.eat();
sheep.eat();
}
}
八、接口
1、概念
接口就是一种规则,是对行为的抽象。
2、定义和使用
1.接口用关键字interface来定义
public interface 接口名 {}
2.接口不能实例化
3.接口和类之间是实现关系,通过implements关键字表示
public class 类名 implements 接口名 {}
4.接口的子类(实现类)
(1)要么重写接口中的所有抽象方法
(2)要么是抽象类
5.注意
(1)接口和类的实现关系,可以单实现,也可以多实现
public class 类名 implements 接口名1,接口名2 {}
(2)实现类还可以在继承一个类的同时实现多个接口
public class 类名 extends 父类 implements 接口名1,接口名2 {}
3、接口中成员的特点
1.成员变量
(1)只能是常量
(2)默认修饰符:public static final
2.构造方法
没有
3.成员方法
(1)只能是抽象方法
(2)默认修饰符:public abstract
4.JDK7以前:接口中只能定义抽象方法
5.JDK8的新特性:接口中可以定义有方法体的方法
6.JDK9的新特性:接口中可以定义私有方法
7.代码示例
public interface Swim {
public abstract void swim();
}
4、接口和类之间的关系
1.类和类的关系
继承关系,只能单继承,不能多继承,但是可以多层继承。
2.类和接口的关系
实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口。
3.接口和接口的关系
继承关系,可以单继承,也可以多继承
4.代码示例
public abstract class Person {
//定义人的属性
private String name;
private int age;
//定义空参构造和带参构造
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
//定义成员变量的get和set方法
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 interface SpeakEnglish {
public abstract void speakEnglish();
}
public abstract class Coach extends Person {
//定义空参构造和带参构造
public Coach() {
}
public Coach(String name, int age) {
super(name, age);
}
//定义抽象方法teach,代表行为:教
public abstract void teach();
}
public class BasketballCoach extends Coach {
//定义空参构造和带参构造
public BasketballCoach() {
}
public BasketballCoach(String name, int age) {
super(name, age);
}
//重写抽象方法teach
@Override
public void teach() {
System.out.println("教打篮球");
}
}
public class TableTennisCoach extends Coach implements SpeakEnglish {
//定义空参构造和带参构造
public TableTennisCoach() {
}
public TableTennisCoach(String name, int age) {
super(name, age);
}
@Override
public void teach() {
System.out.println("教打乒乓球");
}
@Override
public void speakEnglish() {
System.out.println("说英语");
}
}
public abstract class Player extends Person{
//定义空参构造和带参构造
public Player() {
}
public Player(String name, int age) {
super(name, age);
}
//定义抽象方法study,代表行为:学
public abstract void study();
}
public class BasketballPlayer extends Player {
//定义空参构造和带参构造
public BasketballPlayer() {
}
public BasketballPlayer(String name, int age) {
super(name, age);
}
//重写抽象方法study
@Override
public void study() {
System.out.println("学打篮球");
}
}
public class TableTennisPlayer extends Player implements SpeakEnglish {
//定义空参构造和带参构造
public TableTennisPlayer() {
}
public TableTennisPlayer(String name, int age) {
super(name, age);
}
//重写抽象方法study
@Override
public void study() {
System.out.println("学打乒乓球");
}
//重写接口方法speakEnglish
@Override
public void speakEnglish() {
System.out.println("说英语");
}
}
public class Test {
public static void main(String[] args) {
//创建对象
TableTennisPlayer player1 = new TableTennisPlayer("张三",23);
TableTennisCoach coach1 = new TableTennisCoach("lisi",36);
BasketballPlayer player2 = new BasketballPlayer("王五",23);
BasketballCoach coach2 = new BasketballCoach("zhaoliu",34);
//调用方法
player1.speakEnglish();
player1.study();
coach1.speakEnglish();
coach1.teach();
player2.study();
coach2.teach();
}
}
5、新增方法
1.JDK8以后接口中新增的方法1
(1)允许在接口中定义默认方法,需要使用关键字default修饰
作用:解决接口升级的问题
(2)接口中默认方法的定义格式
1)格式:public default 返回值类型 方法名(参数列表){}
2)范例:public default void show(){}
(3)接口中默认方法的注意事项
1)默认方法不是抽象方法,所以不强制被重写。但是如果被重写,重写的时候去掉default关键字。
2)public可以省略,default不能省略
3)如果实现了多个接口,多个接口中存在相同名字的默认方法,子类就必须对该方法进行重写
2.JDK8以后接口中新增的方法2
(1)允许在接口中定义静态方法,需要用static修饰
(2)接口中静态方法的定义格式
1)格式:public static 返回值类型 方法名(参数列表){}
2)范例:public static void show(){}
(3)接口中静态方法的注意事项
1)静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
2)public可以省略,static不能省略
3.JDK9新增的方法
(1)接口中私有方法的定义格式
1)普通的私有方法
格式:private 返回值类型 方法名(参数列表){}
范例:private void show(){}
2)静态的私有方法
格式:private static 返回值类型 方法名(参数列表){}
范例:private static void method(){}
6、接口的应用
1.接口代表规则,是行为的抽象。想要让哪个类拥有一个行为,就让这个类实现对应的接口就可以了。
2.当一个方法的参数是接口时,可以传递接口所有实现类的对象,这种方式称为接口多态。
7、适配器设计模式
1.设计模式(Design pattern)是一套被反复使用】多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。
简单理解:设计模式就是各种套路
2.适配器设计模式:解决接口与接口实现类之间的矛盾问题
3.当一个接口中抽象方法过多,但是我只要使用其中一部分的时候,就可以使用适配器设计模式。
4.书写步骤
(1)编写中间类XXXAdapter,实现对应的接口
(2)对接口中的抽象方法进行空实现
(3)让真正的实现类继承中间类,并重写需要用的方法
(4)为了避免其他类创建适配器类的对象,中间的适配器类用abstract进行修饰
九、内部类
1、概念
在一个类的里面,再定义一个类。
举例:在A类的内部定义B类,B类就称为内部类
2、使用时机
内部类表示的事物是外部类的一部分,且内部类单独存在没有意义。比如,汽车的发动机、ArrayList的迭代器、人的心脏等。
3、访问特点
(1)内部类可以直接访问外部类的成员,包括私有
(2)外部类要访问内部类的成员,必须创建对象
4、分类
成员内部类、静态内部类、局部内部类、匿名内部类
5、成员内部类
1.写在成员位置的,属于外部类的成员。
2.成员内部类可以被一些修饰符所修饰,比如:private、默认、protected、public、static等。
3.在成员内部类里面,JDK16之前不能定义静态变量,JDK16开始才可以定义静态变量。
4.获取成员内部类对象的两种方式
(1)当成员内部类被private修饰时。在外部类编写方法,对外提供内部类对象。
(2)当成员内部类被非私有修饰时,直接创建对象。
外部类名.内部类名 对象名 = new 外部类名().new 内部类名();
5.外部类成员变量和内部类成员变量重名时,在内部类如何访问?
System.out.println(外部类名.this.变量名);
6、静态内部类
1.静态内部类是一种特殊的成员内部类
2.静态内部类只能访问外部类中的静态变量和静态方法,如果想要访问非静态的需要创建对象。
3.直接创建静态内部类对象的方式
外部类名.内部类名 对象名 = new 外部类名.内部类名();
4.调用静态内部类中的方法
(1)非静态方法:先创建对象,用对象调用
(2)静态方法:外部类名.内部类名.方法名();
7、局部内部类
1.将内部类定义在方法里面就叫做局部内部类,类似于方法里面的局部变量。
2.外界是无法直接使用,需要在方法内部创建对象并使用。
3.该类可以直接访问外部类的成员,也可以访问方法内的局部变量。
8、匿名内部类
1.概念
匿名内部类本质上就是隐藏了名字的内部类,可以写在成员位置,也可以写在局部位置。
2.格式
new 类名或者接口名() {
重写方法;
};
3.格式的细节
(1)包含了继承或实现,方法重写,创建对象。
(2)整体就是一个类的子类对象或者接口的实现类对象。
4.使用场景
当方法的参数是接口或者类时, 以接口为例,可以传递这个接口的实现类对象,如果实现类只要使用一次,就可以用匿名内部类简化代码。
总结
在学习了面向对象的进阶知识后,一定要自己独立动手练习代码,为以后的更深入学习打好基础,并且此篇中每一部分的内存图板块一定要好好了解一下。学习好面向对象的编程思想,会极大提高代码的复用性和可读性。
原文地址:https://blog.csdn.net/2301_80709894/article/details/139546668
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!