Java
day8——2023.8.15
super
定义:super在调用的时候,指的是当前类对象的父类
因为父类的属性私有化之后,不能直接被子类调用,这时候就可以通过super关键字完成调用
super可以用来调用父类的属性,成员方法还有构造方法
super只能出现在子类的普通方法和构造方法中
super在子类中构造方法中,只能出现在第一行代码中
super调用父类的属性
super调用父类的属性,不能调用父类的私有属性,所以将来用super调用父类属性的用法其实很少用,只能调用没有私有化的属性
子类通过super调用父类的构造方法
写在子类构造方法的第一行
写法 : super();
1,关于无参构造的调用,子类的无参构造会默认访问父类的无参构造
如果子类的有参构造,没有显示的访问父类的有参构造,其实也会默认访问父类的无参构造
为什么?
因为子类继承父类、可能会使用父类的数据,所以在子类初始化之前,得先完成父类数据的初始化
2,关于有参构造的作用
子类可以通过在super()中传入参数的形式,完成对父类有参构造方法的调用
子类通过super调用父类的普通方法
子类可以在构造方法或者普通方法中,通过super调用父类的普通方法
写法 : super.方法名();
public class Person {
String name; //姓名
int age; //年龄
String gender; //性别
//父类的无参构造被注释,子类就无法使用父类的无参构造了
public Person() {
}
public Person(String name, int age, String gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void show(){
System.out.println("我的姓名:" + name
+ ",我的年龄:" + age
+ ",我的性别:" + gender);
}
}
public class Emp extends Person{ //员工类
String deptName; //部门名称,一种扩展
public Emp() {
//子类的无参构造,默认会去访问父类的无参构造
super();
super.show();
}
public Emp(String name, int age, String gender, String deptName) {
super(name,age,gender);
//super(name, age);
this.deptName = deptName;
}
public void show() {
// 通过super调用父类的普通方法
super.show();
System.out.println("我所在的部门:" + deptName);
}
}
public class TestEmp {
public static void main(String[] args) {
Emp emp = new Emp();
emp.deptName = "研发部";
emp.name = "张三";
emp.age = 20;
emp.gender = "男";
emp.show();
Emp emp1 = new Emp("李四", 22, "男", "财务部");
emp1.show();
}
}
创建一个动物类,作为父类 ,有私有化 种类、颜色两个属性,有一个show方法,作为动物的介绍 介绍内容为 : 我的种类是:xx,我的颜色是:xx 声明一个 小狗类,作为子类,有姓名 私有化属性作为扩展 ,子类也有一个show方法,并对父类的show方法做一个扩展,添加一条输出语句: 我的名字是:xx 要求 :通过全参的构造方法,实例化一个狗类对象,并调用show方法
package com.iweb.airui369.day07;
public class Animal {
private String kind;
private String color;
public Animal() {
}
public Animal(String kind, String color) {
this.kind = kind;
this.color = color;
}
public void show(){
System.out.println("我的品种是:" + kind + ",我的颜色是:" + color);
}
}
package com.iweb.airui369.day07;
public class Dog extends Animal{
private String name;
public Dog(String kind, String color, String name) {
super(kind, color);
this.name = name;
}
@Override
public void show() {
super.show();
System.out.println("我的名字是:" + name);
}
}
package com.iweb.airui369.day07;
public class Main {
public static void main(String[] args) {
Dog dog = new Dog("柯基","黑色","旺财");
dog.show();
}
}
super和this的区别
this指的是当前对象的引用
super指的是父类对象引用
super关键字:
super.父类属性名: 调用父类的属性
super.父类方法名: 调用父类的方法
super() : 调用父类的无参构造函数
super(参数): 调用父类有参构造函数
this的用法 :
this.属性名 :调用当前对象的属性 (局部变量和成员变量冲突)
this.方法名 : 调用当前对象的方法
this() : 调用当前类的无参构造
this(参数) : 调用当前类的有参构造
如果构造函数的第一行不是this()或者super()。系统默认添加super()
this和super关键字,不能共存
方法重写
概念:子类继承父类,子类重写父类的方法,方法名相同,参数列表相同,返回值类型相同或者是父类方法返回值的子类,访问修饰符不能严于父类,不能抛出比父类更多的异常
父类的私有方法不能被重写
父类的静态方法,不能被子类重写,
如果子类定义了和父类相同的静态方法或者私有方法,都会被当做子类自己的方法
子类为什么要重写父类方法?
一般是因为父类方法的功能不能满足子类的需求,然后子类对于父类方法做出扩展
package com.iweb.airui369.day07;
public class Father {
public void method01(){
System.out.println("这是父类的普通方法");
}
public Father method02(){
return new Father();
}
public void method03(int a,int b){
System.out.println("父类中带参数的方法");
}
public static void method04(){
System.out.println("父类的静态方法");
}
private void method05(){
System.out.println("父类的私有方法");
}
}
package com.iweb.airui369.day07;
public class Son extends Father{
@Override
public void method01() {
System.out.println("这是子类的方法");
}
//子类重写的方法的返回值,可以和父类相同
//或者是父类返回值的子类
@Override
public Son method02() {
return new Son();
}
//@Override //用来标识子类的方法重写了父类方法的
//method03参数和父类的参数不一样,这个方法仍然可以声明
//但是这个时候,它和父类方法没有关系,
// 只是子类自己的一个普通方法
public void method03(int a) {
System.out.println("子类的方法");
}
//@Override
//子类可以声明和父类相同名称的静态方法,
//但是它们之间的关系并不是重写,
//一般子类很少去写和父类同名的静态方法,除非想要将
//父类的静态方法在子类中隐藏
public static void method04(){
System.out.println("子类的静态方法");
}
//@Override
//父类的私有方法不能被子类重写
private void method05(){
System.out.println("子类重写父类的私有方法");
}
}
方法重写的应用
equals()方法重写
public class Student {
private int sid; //学号
private String name; //姓名
private String className; //班级
public Student() {
}
public Student(int sid, String name, String className) {
this.sid = sid;
this.name = name;
this.className = className;
}
@Override
public boolean equals(Object obj) {
//如果比较的两个对象,地址值相同,它们就是同一个对象
if (this == obj){
return true;
}
//如果传入的参数,不是一个Student对象,直接返回false
//通过 instanceof 可以判断某个对象是否是指定类的实例
// 用法 : 对象名 instanceof 类
if (!(obj instanceof Student)){
return false;
}
//判断传入的对象的属性和当前对象的属性的值,是否都是相同的
//传入的参数会被当做是Object类型,将传入的参数,转为Student
Student o = (Student) obj;
if (this.sid == o.sid &&
this.name.equals(o.name) &&
this.className.equals(o.className)){
return true;
}
return false;
}
@Override
public String toString() {
return "Student{" +
"sid=" + sid +
", name='" + name + '\'' +
", className='" + className + '\'' +
'}';
}
}
public class StudentTest {
public static void main(String[] args) {
//创建两个相同对象
Student s1 = new Student(10010, "张三", "1班");
Student s2 = new Student(10010, "张三", "1班");
System.out.println(s1);
//使用== 比较引用类型,比较的是地址值,返回false
System.out.println(s1 == s2); //false
//在Object类中,提供了一个equals()方法,用来比较对象之间是否相同的
//将来,如果想比较对象是否相同,那么就得用equals()方法
//两个相同的对象,我们调用equals比较,希望返回true,但是返回false
//这是因为Object类中的equals()方法,底层代码还是== 做比较,
//如果想要equals()方法可以比较对象的具体的属性,就需要重写equals()方法
System.out.println(s1.equals(s2));
}
}
toString()方法
当对象在被输出的时候,默认的会访问这个对象类的toString()方法,如果类中没有,就默认访问Object类中的toString()方法。
Object类中的toString()方法如下,表示返回当前类的包名+类名 +@+当前对象hashCode值的16进制
所以输出对象的时候,最后的结果就是一串值
所以将来,类中,一般都会重写toString()方法
相关面试题
1.重载和重写的区别
重载是指在同一个类中,根据参数的类型、个数或顺序,定义多个同名但签名不同的方法。重载的方法可以有不同的返回类型。重载通常用于提供相似但功能不同的操作。
重写是指在继承关系中,子类重新定义(覆盖)了父类中已有的方法。重写要求方法名、参数列表和返回类型完全相同。通过重写,子类可以改变父类方法的实现,从而实现自己的特定行为。
重载是在同一个类中根据参数的不同定义多个同名方法,用于提供相似但功能不同的操作;而重写是子类重新定义并覆盖父类中已有的方法,用于改变方法的实现。
2.==和equals()方法的区别
== 可以用来比较基本类型和引用类型
比较基本类型的时候,比较的是值
比较引用类型的时候,比较的是对象的地址值
equals()方法,是Object类中的方法,一般用来比较对象是否相同
底层代码其实还是 ==,所以如果想要使用equals()方法,一般会重写equals()方法
重写就是将对象的属性挨个比较,看看是否相同,重写之后可以用来比较对象是否相同
String类的equals()方法
重写了Object类中的equals()方法,底层的原理就是将需要比较字符串转为字符数组之后,利用循环,一个个比较数组中的字符值是否相同
3.为什么重写equals()方法,一定要重写hashCode()方法?
因为在Java中,相同的对象,它的hashCode值必须也是相同的
所以在重写equals()方法的同事,要重写hashCode()方法,为了保证他们的hashCode值也相同
抽象类
概念
继承中,将多个共同的内容,提取到一个类中,但是有些共同方法,方法声明是一样的,具体的实现不一样(每个具体的对象在做具体的操作的时候,功能实现不同),如果出现这种情况,这个方法就需要被定义的抽象方法
抽象方法就是具有方法名,但是没有具体的方法实现(没有大括号)
如果一个类中存在抽象方法,那么该类 就是抽象类
抽象类特点
写法:抽象方法或者抽象类通过abstract关键字修饰
特点:抽象类不能实例化
抽象类中,可以没有抽象方法,但是有抽象方法的类一定是抽象类
抽象类的子类,必须是一个抽象类或者是一个普通类,普通类必须重写抽象类中的所有抽象方法
抽象类的成员属性
成员属性 : 可以有变量,也可以有常量
成员方法:有抽象方法,普通方法,静态方法
构造方法 :有构造方法,抽象类不能实例化,所以构造方法一般提供给子类访问使用
public abstract class Animal {
private String name;
final int age = 20;
public Animal(String name) {
this.name = name;
}
public Animal() {
}
//抽象方法,声明为抽象方法的类,必须是抽象类
// 加上abstract关键字,完成方法和类的声明
public abstract void eat();
public void sleep(){
System.out.println("动物会睡觉!");
}
public static void show(){
System.out.println("动物会说话!");
}
}
//子类继承抽象类之后,子类要么是抽象类,要么就要实现父类中的所有抽象方法
public class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼~");
}
}
public class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗吃骨头~");
}
}
public class AnimalTest {
public static void main(String[] args) {
Cat cat = new Cat();
cat.eat();
cat.sleep();
cat.show();
Dog dog = new Dog();
dog.eat();
dog.sleep();
dog.show();
//抽象类不能实例化
//Animal animal = new Animal();
//抽象类中可以定义静态方法,也可以通过类名直接调用
Animal.show();
}
}
多态
概念
一个对象,在不同时刻体现出来的不同状态
Java中多态的表示方式
父类的引用指向子类的对象
父类 对象名 = new 子类();
public class Person {
public void say(){
System.out.println("人类会说话!");
}
public void show(){
System.out.println("这是父类的show方法");
}
}
public class Student extends Person {
@Override
public void show(){
System.out.println("这是子类的show方法");
}
public void show1(){
System.out.println("这是子类特有的show1方法");
}
}
public class PersonTest {
public static void main(String[] args) {
//多态创建Person对象
//父类的引用指向子类的对象
Person person = new Student();
person.say(); //作为父类形态,可以调用父类特有的方法
// person.show1(); //不能调用子类特有的方法
person.show(); // 作为子类形态,可以调用子类重写父类的方法
}
}
多态的特点
前提条件:1.有继承或者实现关系
2.有方法的重写
3.有父类或者父接口的引用指向子类对象
多态的分类
1,具体类的多态
class Fu{}
class Zi extends Fu{}
Fu f = new Zi();
2,抽象类的多态
abstract class Fu{}
class Zi extends Fu{}
Fu f = new Zi();
3,接口多态
interface Fu{}
class Zi implements Fu{}
Fu f = new Zi();
多态关系中成员访问特点
成员变量 :直接看等号的左边,左边是谁,优先找谁,没有向上找,找不到就报错
编译看左边,运行看左边
成员方法:先从等号的左边找,左边不存在,就报错,运行的时候看new的是谁,就找谁
编译看左边,运行看右边
构造方法:子类的构造默认会访问父类构造
多态的好处
多态可以提高代码的维护性(继承)
多态可以提高代码扩展性(多态体现)
public abstract class Emp {
public abstract void work();
}
public class Teacher extends Emp{
@Override
public void work() {
System.out.println("老师在讲课!");
}
}
public class Assistant extends Emp {
@Override
public void work() {
System.out.println("助教在辅导");
}
}
public class EmpTest {
public static void main(String[] args) {
//没有多态,创建对象,关心的是通过哪个类创建的对象调用的work方法
Teacher emp1 = new Teacher();
emp1.work();
Assistant emp2 = new Assistant();
emp2.work();
//多态的方式创建,关注点是work方法的调用,其他的东西我不关心
Emp emp3 = new Teacher();
emp3.work();
Emp emp4 = new Assistant();
emp4.work();
}
}
多态的缺点
父类引用不能使用子类特有功能,子类可以当做父类使用,父类不能当做子类使用
多态的转型
多态中,父类引用不能使用子类特有功能,所以,要将父类对象做转型
多态的转型,分为向上转型和向下转型
多态的分类
面向对象中,根据代码执行时刻的不同,将多态分为 编译时多态 和 运行时多态
编译时多态是静态的,一般指的是方法的重载,根据参数列表来区分不同的方法,通过编译之后就会变成不同的方法
运行时多态是动态的,它是通过动态绑定来实现的,通过方法的重写来体现的,也就是之前讲解的面向对象的多态性
练习
请创建一个Animal动物类,要求有方法eat()方法,方法输出一条语句“吃东西”。
创建一个Bird类继承Animal类,重写eat()方法输出一条语句“鸟儿 吃虫”,特有方法fly()输出鸟儿飞翔"
在Test类中向上转型创建bird对象,调用eat()方法。然后向下转型调用fly()方 法。
public class Animal {
public void eat(){
System.out.println("吃东西");
}
}
public class Bird extends Animal {
@Override
public void eat() {
System.out.println("鸟儿 吃虫");
}
public void fly(){
System.out.println("鸟儿 飞翔");
}
}
public class Test {
public static void main(String[] args) {
Animal bird = new Bird();
bird.eat();
Bird bird2 = (Bird) bird;
bird2.fly();
}
}
final关键字
概念 :final表示最终的意思,可以用来修饰类、方法、变量
特点 :
1,final修饰的类,不能被继承 (String类不能被继承)
2,final修饰的方法,该方法不能被重写
3,final修饰的变量,该变量不能被重新赋值,final修饰的变量就是常量,一般final修饰的变量需要指定初始值
public class Father {
public int num = 10; //普通的变量
public final int num2 = 20; //常量
public final void test01(){
System.out.println("父类的final方法");
}
}
public class Son extends Father {
String name;
public void show(){
num = 100;
//num2是常量,无法重新赋值
//num2 = 1000;
System.out.println(num);
System.out.println(num2);
}
//子类不能重新父类的final方法
//public void test01(){
// System.out.println("子类的方法");
//}
}
public class Test {
public static void main(String[] args) {
Son son = new Son();
son.show();
final int a = 10; //final修饰的局部变量也是常量
// a = 100; //不能被重新赋值
System.out.println(a);
Son son1 = new Son();
son1.name = "张三";
System.out.println(son1.name);
final Son son2 = new Son();
son2.name = "李四";
System.out.println(son2.name);
//final修饰的引用类型变量,变量中的属性值是可以被改变的
son2.name = "王五";
System.out.println(son2.name);
//final修饰的引用的类型的变量,引用地址无法重新被赋值
//son2 = new Son();
}
//final能不能修饰局部变量?修饰基本类型和引用类型的区别
//final修饰的局部变量也是常量
//final修饰的引用类型变量,变量中的属性值是可以被改变的
//final修饰的引用的类型的变量,引用地址无法重新被赋值
}