IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Java面向对象 -> 正文阅读

[Java知识库]Java面向对象

面向对象



定义

把数据及对数据的操作方法放在一起,作为一个相互依存的整体——对象。对同类对象抽象出其共性,形成类。类中的大多数数据,只能用本类的方法进行处理。类通过一个简单的外部接口与外界发生关系,对象与对象之间通过消息进行通信。程序流程由用户在使用中决定。对象即为人对各种具体物体抽象后的一个概念,人们每天都要接触各种各样的对象,如手机就是一个对象。


类与对象

对象:具体的事物,具体的实体(有状态(属性),有行为(方法))。例如:学生是一个对象,状态:age,name,height…行为:吃饭,睡觉,打游戏…

:对对象向上抽取出像 的部分,形成类,类是抽象的,是一个模板。例如:Student是一个对象,Teacher也是一个对象,他们共同属性是:age,name,height,共同行为有:吃饭,睡觉,那么就可以向上抽取共同的部分创建一个Person类,被创建的Person又可以创建无数个对象,如:员工对象,他拥有的属性:name,age。行为:吃饭睡觉(当然一个具体的对象还可以拥有自己独特的属性和行为,类只是一个创建对象的模板,它是抽象的)


面向对象三个阶段

OOA:面向对象分析(Object Oriented Analysis)

OOD:面向对象设计(Object Oriented Design)

OOP:面向对象编程(Object Oriented Programming )


类与对象的创建

创建类

  • 属性(成员变量)

    定义该类或该类对象包含的数据或则静态特征。范围:整个类体

    格式:

    [修饰符] 属性类型 属性名=[默认值];

  • 方法

    定义该类或该类实例的行为特征和功能实现。作用:重用代码,封装,便于维护修改

    格式:

    [修饰符] 方法返回值类型 方法名(形参列表){

    ? 方法体;

    }

代码

  1. 创建一个Person类:

    public class Person {
        //属性:
        int age;
        String name;
        //方法:
        //无返无参
        public void eat(){
            System.out.println("吃饭");
        }
        //无返有参
        public void sleep(String address){
            System.out.println("我在"+address+"睡觉");
        }
        //有返无参
        public String introduce(){
            return "名字:"+name+",年龄:"+age;
        }
    
    }
    
  2. 专门创建一个Test类(用于完成业务逻辑代码)

    public class Test {
        public static void main(String[] args) {
            //第一次加载类的时候会进行类的加载,初始化对象的时候,对象的属性没有给赋值有默认初始化值
            Person  sh= new Person();//创建一个对象,名字叫sh
            //给属性初始化
            sh.age=22;
            sh.name="苏欢";
            //可以在创建一个对象,名字叫yu
            //第二次创建对象的时候不用类的加载(类的加载只进行一次)
            Person yu = new Person();
            //给属性初始化
            yu.name = "李雨";
            yu.age = 20;
            //对属性值进行读取
            System.out.println(sh.name+sh.age+yu.name+yu.age);
            //对方法进行操作
            //不同对象,属性有自己特有的值,但是方法都是调用类中通用的
            //属性:独立的        方法:共享的
            sh.eat();
            yu.eat();
            sh.sleep("我是");//我在卧室睡觉
            yu.introduce();//yu这个对象做自我介绍
            //对返回的字符串做一个接收
            String str = yu.introduce();
            System.out.println(str);
            //或则
            System.out.println(yu.introduce());
        }
    }
    
  3. 总结:

    • 创建对象时会进行类的加载,且只在第一次创建对象时加载一次
    • 属性:通过赋值有自己独立的
    • 方法:对象调用类中通用的方法,即共享的

成员变量和局部变量

成员变量(可以称类的属性)

  1. 位置:在类中方法外
  2. 作用范围:当前类中所有方法里面或则代码块里面
  3. 有默认值

在这里插入图片描述
引用数据类型默认:null

  1. 内存位置:堆中

  2. 作用时间:当前对象创建到销毁

局部变量

  1. 位置:在方法中或则代码块中
  2. 作用范围:当前的方法中或代码块中
  3. 没有默认值,所以必须初始化
  4. 内存位置:栈中
  5. 作用时间:当前方法执行到结束

※构造器

  • 定义
    构造器通常也叫构造方法、构造函数,构造器在每个项目中几乎无处不在。当你new一个对象时,就会调用构造器。构造器格式如下:

    [修饰符,比如public] 类名 (参数列表,可以没有参数){
    	//不能有return
    }
    
  • 默认构造器

    如果没有定义构造器,则会默认一个无参构造器,这就是为什么你定义了一个对象,比如 People,没有定义任何构造器却可以new这个对象,比如 new People() 。如果自定义了构造器,则会覆盖默认构造器。例如:

    public Person(){
        
    }
    
  • 作用

    不是为了创建对象,在调用构造器之前构造器就已经创建好了,并且属性有默认初始化的值,给对象属性进行一系列赋值操作

    Person p = new Person();//创建对象在堆里开辟空间,系统给空间赋一个地址,最后地址给了左侧的p

  • ※构造器重载

    与普通方法一样,构造器也支持重载。一个对象中是可以支持同时定义多个构造器,通过不同的参数列表来实现重载。经常看到代码中new一个对象时,有时传入参数,有时又可以不用传。比如:new Person()跟new Person(“张三 ”),这里就是重载了。

    利用构造器重载给属性赋值:

    //构造器重载
    public Person(String a,int b){
        //赋值
        name = a;
        age = b;
    }
    public Person(String a){
        name = a;
    }
    

    一般的来讲定义形参的时候会要求见名知意,这就出现一个问题,形参名字与属性名重名,例如上面的a,b换成name和age。怎样规避这个问题呢?

    形参不变,属性前加this.

    例如改进上面代码:

    public Person(String name,int age){
        //赋值
        this.name = name;
        this.age = age;
    }
    public Person(String a){
        name = a;
    }
    

    this代表你创建的那个对象(this.name: 当前对象的名字)

  • 注意

  1. 没有返回值
  2. 内部不能有return语句
  3. 构造器必须和类名一样
  4. 一般不会在空构造器里面赋初始值,因为每个对象属性都一样了
  5. 与位置顺序无关
  6. 一般保证空构造器的存在
  7. 当重载构造器以后,假如忘记写,系统不会分配默认的空构造器,那么调用就会出错

内存分析

  1. 代码1

     public class Person {
            int  id;
            int  age;
    
            public static void main(String args[]){
                    Person p1= new Person();
            }
    }
    

    分析图
    在这里插入图片描述

  2. 代码2

    public class Person {
            int id;
            int age;
            String school;
            public Person (int a,int b,String c){
                    id=a;
                    age=b;
                    school=c;
            }
            public static void main(String args[]){
                    Person p= new Person(1,20, "海淀");
            }
    }
    

    分析步骤

    1.首先为main方法开辟栈帧

    2.第一次遇见Person时,加载字节码信息,加载到方法区

    3.根据字节码信息在堆中创建对象

    4.系统会进行默认初始化

    5.调用new关键字,进入构造器进行赋值

    6.为Person构造器开辟栈帧

    7.将1赋值给a,20赋值给b,而字符串比较特殊,方法区中有字符串常量池专门放字符串

    遇到“海淀”时,将“海淀”放入字符串常量池中对应产生一个地址

    8.进入构造器进行一系列赋值操作

    9.构造器结束,产生的形参,局部变量都要消失

    10.对象创建完成,赋值完成

    11.将左侧赋值给右侧的p,p在main方法对应的栈帧中

    分析图
    在这里插入图片描述

  3. 代码3

    class Person{
            int id;
            int age;
            String school;
            Person (int a,int b,String c){
                    id=a;
                    age=b;
                    school=c;
            }
    
            public void setAge(int a){
                    age=a;
            }
    }
    
    public class Test {
        public static void main(String[] args) {
                      Test t=new Test();
                      int age=40; 
                      Person tom=new Person(1,20,"海淀");
                      Person jack=new Person(2,30,"朝阳");
                      t.change1(age);
                      t.change2(tom);
                      t.change3(jack);
                      System.out.println(age); //40
                      System.out.println("id:"+jack.id+",age:"+jack.age+",school:"+jack.school); //id:2,age:66,school:"朝阳"
        }
        public void change1(int i){
                    i=3366;
        }
    
        public void change2(Person p){
                  p=new Person(3,22,"西城");
        }
    
        public void change3(Person p){
            p.setAge(66);
        }
    
    }
    

    分析图
    在这里插入图片描述


this

创建对象的过程

  1. 在第一次遇到一个类的时候,对这个类要进行加载,只加载一次。
  2. 创建对象,在堆中开辟空间
  3. 对对象进行初始化操作,属性赋值都是默认的初始值。
  4. new关键字调用构造器,执行构造方法,在构造器中对属性重新进行赋值

用法

1.this修饰属性

当属性名字和形参发生重名的时候,或者 属性名字 和局部变量重名的时候,都会发生就近原则,所以如果我要是直接使用变量名字的话就指的是离的近的那个形参或者局部变量,这时候如果我想要表示属性的话,在前面要加上:this.修饰

2.this修饰方法

public void play(){
    //System.out.println("打游戏");
    this.game();//等效上面一行代码
    System.out.println("上网");
    System.out.println("洗澡");
}
public void game(){
    System.out.println("打游戏");
}

3.this修饰构造器

    //空构造器
    public Person(){

    }
    //三个参数构造器
    public Person(int age,String name,double height){
       this(age,name);//调用两参构造器
        this.height=height;
    }
    public Person(int age,String name){
        this(age);//调用一个参数构造器
        this.name=name;
    }
    public Person(int age){
        this.age=age;
    }

总结:同一个类中的构造器可以相互用this调用,注意:this修饰构造器必须放在第一行


static

1.static修饰属性

public class Test {
    //属性:
    int id;
    static int sid;
    //这是一个main方法,是程序的入口:
    public static void main(String[] args) {
        //创建一个Test类的具体的对象
        Test t1 = new Test();
        t1.id = 10;
        t1.sid = 10;
        Test t2 = new Test();
        t2.id = 20;
        t2.sid = 20;
        Test t3 = new Test();
        t3.id = 30;
        t3.sid = 30;
        //读取属性的值:
        System.out.println(t1.id);
        System.out.println(t2.id);
        System.out.println(t3.id);
        System.out.println(t1.sid);
        System.out.println(t2.sid);
        System.out.println(t3.sid);
    }
}

分析图
在这里插入图片描述
一般官方的推荐访问方式:可以通过类名.属性名的方式去访问:

  Test01.sid=100;
        System.out.println(Test01.sid);

总结

  1. 在类加载的时候一起加载入方法区中的静态域中
  2. 先于对象存在
  3. 访问方式: 对象名.属性名 类名.属性名(推荐)

static修饰属性的应用场景:某些特定的数据想要在内存中共享,只有一块 --》这个情况下,就可以用static修饰的属性

public class MsbStudent {
    //属性:
    String name;
    int age;
    static String school;
    //这是一个main方法,是程序的入口:
    public static void main(String[] args) {
        MsbStudent.school = "马士兵教育";
        //创建学生对象:
        MsbStudent s1 = new MsbStudent();
        s1.name = "张三";
        s1.age = 19;
        //s1.school = "马士兵教育";
        MsbStudent s2 = new MsbStudent();
        s2.name = "李四";
        s2.age = 21;
        //s2.school = "马士兵教育";
        System.out.println(s2.school);
    }
}

属性:
静态属性 (类变量)
非静态属性(实例变量)

2.static修饰方法

package com.suhuan.Test02;

/**
 * @Auther: suhuan
 * @Date: 2021/7/21 - 07 - 21 - 23:45
 */
public class Demo {
    int id ;
    static int sid;
    public void a(){
        System.out.println("a方法");
    }
    //1.public和static都是修饰符,没有先后顺序的说法
    public static void b(){
        System.out.println("b方法");
        //System.out.println(id);//2.在静态方法中不能访问非静态属性(原因:static修饰的东西先于
        // 对象存在,在类加载的时候就被加载,static加载完了还没有对象,b方法就可以直接调用,不用依托对象去调用
        // id是属于对象特有的属性,所以没对象就去访问id是不合理的)
        //a();  3.也是不能访问的
        //4.在静态方法中不能访问this关键字
        // 总结:
        // static修饰的想要访问就必须要找static修饰的东西
    }

    public static void main(String[] args) {
        //调用方法必须创建对象,用对象去调用
        Demo d = new Demo();
        d.a();
        //5.非静态方法可以用类名去调用
        //6.静态方法对象和类名都可以调用
        //而b方法可以用类名去调用
        Demo.b();
        d.b();//有对象也可以用对象调用(不推荐)
        //在同一个类中可以直接调用,因为main方法也是静态的
        b();
    }
}

总结归纳

  1. public和static都是修饰符,没有先后顺序的说法

  2. 在静态方法中不能访问非静态属性(原因:static修饰的东西先于对象存在,在类加载的时候就被加载,static加载完了还没有对象,b方法就可以直接调用,不用依托对象去调用id是属于对象特有的属性,所以没对象就去访问id是不合理的)

  3. 静态方法中不能访问非静态方法

  4. 在静态方法中不能访问this关键字

  5. 非静态方法可以用类名去调用

  6. 静态方法对象和类名都可以调用


代码块

类的组成:属性,方法,构造器,代码块内部类

代码块分类:

1.普通块

位置:在方法中

作用:限制了局部变量的作用范围

2.构造块

位置:方法外

作用:解决在方法外写代码的能力

3.静态块

位置:方法外,加static修饰

注意:在静态块中只能访问静态属性和静态方法

4.同步块(多线程)

public class Test {
    int a;
    static int sa;
    public void a(){
        System.out.println("业务逻辑1");
        {
            //1.普通块
            int a=10;
            System.out.println(a);
        }
    }
    //System.out.println("123");//方法外是不允许写代码的
    //构造块
    {
        System.out.println("123");
    }
    //静态块
    static{
        System.out.println("静态块");
        //在静态块中只能访问静态的东西
        System.out.println(sa);
        b();
    }
    public static void b(){
        System.out.println("业务逻辑2");
    }
    public Test(){

    }
    public Test(int a){
        this.a=a;
    }

    public static void main(String[] args) {
        Test t = new Test();
        t.a();
        Test t1 = new Test();
        t1.a();
    }

}

总结

代码块的执行顺序:(由上往下)

  • 静态块(只在类加载的时候执行一次,所以以后实战写项目,数据库的初始化信息都放入静态块,一般要用于执行一些全局性的初始化操作)
  • 构造块
  • 构造器
  • 普通块

包,import

1.包的作用:

为了解决重名问题(实际上包对应的就是盘符上的目录)
解决权限问题

2.包名定义:

  1. 名字全部小写
  2. 中间用.隔开
  3. 一般都是公司域名倒着写 : com.jd com.msb
  4. 加上模块名字:com.jd.login com.jd.register
  5. 不能使用系统中的关键字:nul,con,com1—com9…
  6. 包声明的位置一般都在非注释性代码的第一行

3.导包问题

在创建对象的时候,如果在同一个类下不用导包,如果没有在同一个类下就要自动导包或则手动导包

4.总结

总结:
(1)使用不同包下的类要需要导包: import **..; 例如:import java.util.Date;
(2)在导包以后,还想用其他包下同名的类,就必须要手动自己写所在的包。
(3)同一个包下的类想使用不需要导包,可以直接使用。
(4)在java.lang包下的类,可以直接使用无需导包:

(5)IDEA中导包快捷键:alt+enter ,可以自己设置自动导包

(6)可以直接导入*

(7)在Java中的导包没有包含和被包含的关系

5.静态导入

package com.suhuan.Test03;
import static java.lang.Math.*;//导入java.lang下的Math类中的的所有静态的内容
/**
 * @Auther: suhuan
 * @Date: 2021/7/22 - 07 - 22 - 1:01
 */
public class Demo {
    public static void main(String[] args) {
        System.out.println(random());
        System.out.println(PI);
        System.out.println(round(2.3));
    }
       //在静态导入后,同一个类中有相同的方法的时候,会优先走自己定义的方法。
    public static int round(double a){
        return 100;
    }
}

三大特性

封装 (Encapsulation)

定义:面向对象的封装就是把描述一个对象的属性和行为的代码封装在一个类中,有些属性是不希望公开的,或者说被其他对象访问的,所以我们使用private修饰该属性,使其隐藏起来;类中提供了方法(用public修饰),常用的是getter、setter方法,可以操作这些被隐藏的属性,其他类可以通过调用这些方法,改变隐藏属性的值。

目的:增强安全性和简化编程,使用者不必在意具体实现细节,而只是通过外部接口即可访问类的成员。实现软件部件的**“高内聚、低耦合”**,防止程序相互依赖性而带来的变动影响。在面向对象的编程语言中,对象是封装的最基本单位,面向对象的封装比传统语言的封装更为清晰、更为有力。

构造器可以赋值,setter也可以赋值,那么他们俩的区别是什么呢?

构造器只是仅仅对对象的属性进行一些列的赋值操作而没有对属性进行限制作用,而setter方法可以对属性不仅可以对属性进行赋值而且还可以进行限制

例如:通过构造器给age赋值150那么打印的结果也是150,肯定是不合理的,想要合理怎么做呢?

在构造器里面利用当前对象(this)调用setter方法进行限制

public class Student {
    //属性私有化
    private String name;
    private int age;
    private double height;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        if(age<120&&age>0){
            this.age=age;
        }else{
            this.age=3;
        }
    }
    public double getHeight() {
        return height;
    }

    public void setHeight(double height) {
        this.height = height;
    }
    //构造器
    public Student() {
    }
    public Student(String name, int age, double height) {
        this.name = name;
        this.setAge(age);
        this.height = height;
    }
}

public class Test {
    public static void main(String[] args) {
        Student s = new Student();
        //利用setter方法对属性赋值
        s.setAge(100);
        System.out.println(s.getAge());
        //用三参构造器进行赋值
        Student s1 = new Student("李四",1560,178.3);
        //System.out.println(s1.name);//name被private私有化了没法直接访问只能通过外部的接口(getter)进行访问
        System.out.println(s1.getName()+s1.getAge()+s1.getHeight());
    }
}

继承 ( inheritance)

定义:多个类具有共同的属性(成员变量)与行为(成员方法)的时候,将这些共同的部分向上抽取出来定义到一个公共的类中,其他及各类可以与这个公共的类形成继承关系,从而在多个类中不需要重 复定义公共部分!这个公共的类就是父类,也称为超类或者基类,其他的类就是子类。子类可以直接访问父类的非私有化成员变量,访问父类的私有化成员变量可以使用super.get()方法。父类封装的东西子类也继承了,只是间接调用而已。

**简单的说:**继承是类与类的一种关系,比较像集合中的从属于关系。例如:Student(属性:年龄,姓名,身高。方法:吃饭,睡觉)属于Person,Teacher(属性:年龄,姓名,身高。方法:吃饭,睡觉)也属于人类。把他们共同的部分抽取出来定义到Person类中,那么Student类和Teacher类就是Person类的子类(派生类),Person类就是Student类和Teacher类的父类(基类)。在Java中是单继承的,也就是说一个子类只有一个父类。让他们的关系是子类 is a父类的关系。

//父类
public class Person {
    //属性私有化
    private int age;
    private String name;
    private double height;
    //暴露接口,提供setter,getter方法

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getHeight() {
        return height;
    }

    public void setHeight(double height) {
        this.height = height;
    }
    //方法(人类的行为)
    public void eat(){
        System.out.println("吃饭");
    }
    public void sleep(){
        System.out.println("睡觉");
    }
}
// 子类
public class Student extends Person {
    //Student 继承Person下的属性和方法就用重复定义,只定义特有的属性和方法
    private int sno;

    public int getSno() {
        return sno;
    }

    public void setSno(int sno) {
        this.sno = sno;
    }
    public void study(){
        System.out.println("学习");
    }
}
//Test类
public class Test {
    public static void main(String[] args) {
        //创建子类Student的对象
        Student s = new Student();
        //对属性进行赋值(set方法)
        s.setSno(1001);
        s.setAge(22);
        s.setHeight(187.2);
        s.setName("李四");
        //读取
        System.out.println(s.getName()+"...");
        //访问方法
        s.eat();//父类
        s.sleep();//父类
        s.study();//子类
    }
}

内存分析图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2hJQjDgC-1626968413996)(D:\截图\6.png)]

优点:减少代码量,能很好的提高复用率。使类与类之间存在继承关系,是实现多态操作的前提。

缺点:继承使得多个类之间具有了子父类关系,当一个类存在多个子类的时候,如果父类发生变化,那么这些子类会跟着一同变化,造成类与类之间的“强耦合”关系。

总结

  1. 执行构造方法创建对象完成对象的初始化时,先执行父类的构造,完成父类的初始化,再执行本类的初始化工作。
  2. 第一次创建Person(父)类,是直接继承Object,而Object是jdk提供的工具类,自然不会有属性,所以Object类只提供了无参的构造,在执行Person方法时,无论是无参还是有参都会先执行父类Object的无参构造。
  3. 但是对于Person的子类Student,必须也要有自己的构造方法,由于构造执行的特性,对于无参构造,先执行了Student的无参构造,对于有参构造,则先执行父类Student的有参,然后才执行本类属性的初始化。
  4. 构造方法constructor中,无论是否显式还是隐式调用super(),子类在创建对象调用时都会执行super();
  5. 显式调用构造是在需要通过父类的带参构造来完成子类的带参构造。
  6. 对于父类私有的属性,即使子类继承,但是在test.java中,子类是无法直接访问的,在制定子类的带参构造方法时,只能通过调用父类的带参构造来完成。对于父类非私有的属性,子类对象可以直接调用。
  7. 继承具有传递性,Student继承Person,Person继承Object(所有类的根基)

多态 (polymorphism)


权限修饰符

在这里插入图片描述

private

public class A {
    private int age;
    public void eat(){
        System.out.println(age);
    }
}
public class Test {
    public static void main(String[] args) {
        A a = new A();
        a.age();//不能访问age
    }
}

同一类中可以访问,不同类中不能直接访问

default(缺省)

一般不写

同一个类中,同一个包中可以访问布尔default修饰的属性

protected

package com.suhuan.Test06;

/**
 * @Auther: suhuan
 * @Date: 2021/7/22 - 07 - 22 - 17:10
 */
public class A {
   protected int age;
   public void eat(){
       System.out.println(age);//同类可以访问
   }
}
package com.suhuan.Test06;

/**
 * @Auther: suhuan
 * @Date: 2021/7/22 - 07 - 22 - 17:10
 */
public class Test {
    public static void main(String[] args) {
        A a = new A();
        System.out.println(a.age);//同一包中可以访问
    }
}
package com.suhuan.Test05;

import com.suhuan.Test06.A;

/**
 * @Auther: suhuan
 * @Date: 2021/7/22 - 07 - 22 - 17:21
 */
public class Test01 extends A {
    public void a(){
        System.out.println(age);//在不同包下的子类里面可以访问
    }
}

public

整个项目中都可以访问

一般写代码属性用private修饰,方法用public修饰


方法重写

定义:Java程序中,类的继承关系可以产生一个子类,子类继承父类,它具备了父类所有的特征,继承了父类所有的方法和变量。

子类可以定义新的特征,当子类需要修改父类的一些方法进行扩展,增大功能,程序设计者常常把这样的一种操作方法称为重写,也叫称为覆写或覆盖。

要求:子类的方法名字和父类的方法名必须相同,参数列表(个数,类型,顺序)也要和父类方法保持一致

public class Person {
    public void eat(){
        System.out.println("吃");
    }
    public void sleep(){
        System.out.println("睡觉");
    }
}
public class Student extends Person {
    public void study(){
        System.out.println("学习");
    }
    //子类对父类的方法不满意就要在子类中进行重写
    public void eat(){
        System.out.println("吃小龙虾");
    }
}
public class Test {
    public static void main(String[] args) {
        Student s = new Student();
        s.eat();//走到子类重写的方法中
    }
}

内存分析图:

在这里插入图片描述

重载和重写的区别:

重载:同一类中,当方法名相同,形参列表不同的时候,多个方法构了重载

重写:在不同的类中,子类对父类提供的方法不满意的时候要对父类的方法进行重写

英文位置修饰符返回值方法名参数抛出异常方法体
重载overload同一类中无关无关相同不同无关不同
重写override子类父类中父类的权限修饰符低于子类父类的返回值类型大于子类相同相同小于等于不同

【说明】方法重写时:

子类中方法的访问修饰符必须 >= 父类中对应方法的访问修饰符 (访问修饰符大小关系已在本文中列出)

子类中方法的返回值类型可以和被重写方法的返回值类型相同,或者是被重写方法类型的子类型。

即 子类中方法的返回值类型必须 <= 父类中对应方法的返回值类型

以上结论对于抽象方法的实现同样适用。

如果子类将父类中的方法重写了,调用的时候肯定是调用被重写过的方法,那么如果现在一定要调用父类中的方法该怎么办呢?

接下来就要用到super关键字的引用


super

1.super修饰属性,方法

在子类的方法中,可以通过super.属性或则super.方法的方式,显示的去调用父类提供的属性和方法。通常super.默认不写

public class Student extends Person{
    int sid;
    public void stu(){
        System.out.println("Student特有方法");
    }
    public void a(){
        //访问父类属性age,name和方法eat()
        System.out.println(/*super.*/age);
        /*super.*/eat();
    }
}

如果子类中和父类中有同名的属性,在子类方法中调用该属性的时候调用的是子类中的属性,只不过调用的子类属性前this.(当前对象)省略了,如果想要调用父类中的同名属性就必须显示的加上super.进行修饰(显示调用)

public void a(){
    //访问父类属性age,name和方法eat()
    System.out.println(/*super.*/age);//子类
    System.out.println(this.age);//子类
    System.out.println(super.age);//父类
    /*super.*/eat();
}

方法也是同样的道理

public void a(){
    //访问父类属性age,name和方法eat()
    System.out.println(/*super.*/age);
    System.out.println(this.age);
    System.out.println(super.age);
    /*super.*/eat();
    this.eat();//子类方法
    super.eat();//父类方法
}
public void eat(){
    System.out.println("子类吃饭");
}

2.super修饰构造器

在调用子类空构造器的时候,要向上调用父类的空构造器,因为要对子类进行初始化赋值的时候先要把父类进行初始化赋值只不过子类的空构造器中的super();省略而已

public class Student extends Person {
    int sid;
    public Student() {
       /* super();*/
    }
}

而父类中的空构造器中也有父类(Object),省略了super();

public Person() {
    super();
}

也就是说平时我们写的空构造器中第一行都有super();原因就是必须先把父类初始化完成才进行子类的初始化

利用super可以调用父类的有参构造器

public Person(int age, String name) {
    this.age = age;
    this.name = name;
}
public Student(int sid) {
        this.sid = sid;
    }
public Student(int sid,String name,int age){
    //没有super();
    super(age,name);//利用super调用父类有参构造器
    this.sid=sid;
}

【注意】:所有构造器的第一行都有super();如果super已近显示的调用了super父类有参构造器,那么他的第一行就没有默分配的super();了


那么能不能将this.sid=sid;替换成this(sid);呢

显然是不能的,因为super修饰构造器要放在第一行,this修饰构造器也要放在第一行。

图解:

在这里插入图片描述


继承条件下构造方法的执行过程(面试题)

public class Person {
    int age;
    String name;
    //空构造器
    public Person() {
    }

    //两参数构造器
    public Person(int age, String name) {
        super();
        this.age = age;
        this.name = name;
    }
}
public class Student extends Person {
    int sid;

    public Student() {
    }

    public Student(int age, String name, int sid) {
        super(age, name);
        this.sid = sid;
    }
}
public class Test {
    public static void main(String[] args) {
        Student s = new Student(22,"李四",1001);
    }
}

分析图:

在这里插入图片描述


Object类中的一些方法

1.toSring方法

方法的原理:

在这里插入图片描述

现在,使用toString方法的时候,打印出来的东西 “不好看”,对于其他人来说不友好,可读性不好
我们现在是想知道对象的信息,名字,年龄,身高。。。。。。
现在的格式不好:

public class Test {
    public static void main(String[] args) {
        Student s = new Student(19, "lisa", 150.2);
        System.out.println(s/*.toString()*/);//输出引用数据类型的值(地址值)//com.suhuan.Test11.Student@1b6d3586
        //实际上后面省略了.toString()
        System.out.println(s.toString());//com.suhuan.Test11.Student@1b6d3586
        //而toString()方法不是自己定义的而是Object下的方法
    }
}

出现的问题:子类Student对父类Object提供的toString方法不满意,不满意--》对toString方法进行重写

总结:toString的作用就是对对象进行“自我介绍”,一般子类对父类提供的toString都不满意,都要进行重写。

重写快捷键:Alt+Insert

@Override
public String toString() {
    return "Student{" +
            "age=" + age +
            ", name='" + name + '\'' +
            ", height=" + height +
            '}';
}

2.equals方法

作用:比较对象具体内容是否相等,源码中发现:底层依旧是比较的地址值

public boolean equals(Object obj) {
    return (this == obj);
}

那么就要对equals方法在子类中进行重写(快捷键即可)

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    Phone phone = (Phone) o;
    return Double.compare(phone.price, price) == 0 &&
            year == phone.year &&
            Objects.equals(brand, phone.brand);
}

类与类之间的关系

1.将一个类作为另一个类中方法的形参

public void love(Boy boy){//跟一个男孩(泛指)谈恋爱
    System.out.println("男友名字:"+boy.name+"年龄:"+boy.age);
    boy.buy();
}

2.一个类1作为另一个类2的属性

必须要创建这个类1创建具体的对象才能调用

public class Girl {
    String name;
    double height;
    Mom m /*= new Mom()*/;
    public void weChat(){
        m.say();
    }
    public void love(Boy boy){//跟一个男孩(泛指)谈恋爱
        System.out.println("男友名字:"+boy.name+"年龄:"+boy.age);
        boy.buy();
    }

    public Girl(String name, double height) {
        this.name = name;
        this.height = height;
    }

    public Girl() {
    }
}
public class Mom {
    public void say(){
        System.out.println("唠叨");
    }
}
public class Test {
    public static void main(String[] args) {
        Boy b = new Boy(30,"tom");
        Girl g = new Girl("lisa",100.3);
        g.love(b);
        //和妈妈聊天
        g.m = new Mom();//给小女孩的妈妈赋值一个具体的妈妈
        g.weChat();
    }
}

其他的关系

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-07-24 00:03:28  更:2021-07-24 00:04:26 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/7 11:48:01-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码