Java 面向对象 (类和对象、继承、封装、多态、接口、抽象类)一文详解

面向对象

面向对象的本质 :以类的方式组织代码,以对象的形式组织数据

三大特性 :封装、继承、多态

一、类和对象

​ 类是用来对一个实体(对象)来进行描述的,主要描述该实体(对象)具有哪些属性(外观尺寸等),哪些功能(用来干 啥),描述完成后计算机就可以识别了。

比如:洗衣机,它是一个品牌,在Java中可以将其看成是一个类别。类中 存有 这个类的属性,如一个洗衣机的类,其属性可以是:产品品牌,型号,产品重量,外观尺寸,颜色…

​ 这个洗衣机的行为或者说功能可以用 方法来表示 如:洗衣,烘干、定时.。

代码举例:

// 创建一个 人 的类
class Person {
    //属性(字段、成员变量)
    String name; //姓名
    int age;    //年龄,此处不初始化不会报错

    //定义 人 的行为
    public void eat() {
        System.out.println(name + "正在吃饭");
    }

    public void sleep() {
        System.out.println(name + "正在睡觉");
    }
}
public class ClassName {
    public static void main(String[] args) {
        //使用 new 实例化一个 Person类 的 对象
        Person person = new Person();
        //可以通过 . 访问对象中的成员变量
        person.name = "YKH";
        person.eat();
    }
}
  • 静态方法和成员变量可以通过类名直接调用

  • package 表示 “包” : 类所在的包

  • import 表示 “引入” : 引入 类中 需要的 类(PS : import 只能导入一个具体的类不能导入一个具体的包)

    包名必须是小写字母组成

import java.util.*  
//此处 * 叫做通配符 ,直接导入 这个util包中所有的类,但是建议最好写出要导入的具体的类名,因为 在不同的包下会存在相同名字的类名,当两个包都导入时,就会发生错误。
二、static关键字的用途

一句话描述就是:方便在没有创建对象的情况下进行调用(方法/变量)。

  • 被static关键字修饰的方法或者变量. 既可以通过对象访问,也可以通过类名访问,但一般更推荐使用类名访问。

  • static关键字修饰的方法或者变量,不需要依赖于对象来进行访问,只要类被加载了,就可以通过类名去进行访问。

    (static 修饰的变量或者方法是一开始就和类一起加载的,所以可以直接通过类名去访问,不用实例化)

  • static可以用来修饰类的成员方法、类的成员变量,其修饰的成员方法和变量不依赖于对象

代码举例:

public class test02 {
    public static void main(String[] args) {
        //对于静态修饰的类中的成员变量可以直接通过 类名.成员名 访问
        Person.age = 20;
        // Person 类中的sleep方法和name变量 未加static ,需要先实例化(new)一个对象才能使用
        Person person = new Person();
        person.name = "路飞";
        person.sleep();

        //Person 类中的eat 方法加了static 可以直接用类名进行访问使用
        Person.eat();
    }
}
class Person {
    String name;
    static  int age;
    public static void eat() {
        System.out.println( "正在吃饭");
    }
    public void sleep() {
        System.out.println(name+"正在睡觉");
    }
}

那 既然static 修饰的变量和方法这么方便,那我们为什么不全都加上呢?

原因:

  1. 静态方法没有隐藏的this引用参数,因此不能在静态方法中访问任何非静态成员变量,这是很不方便的,需要通过实例化对象,通过对象来访问成员和方法。
public static String getClassRoom(){
System.out.println(this);
return classRoom;
}
// 编译失败:Error:(35, 28) java: 无法从静态上下文中引用非静态 变量 this
public static String getClassRoom(){
age += 1;
return classRoom;
}
  1. 静态方法中不能调用任何非静态方法,因为非静态方法有this参数,在静态方法中调用时候无法传递this引用
public static String getClassRoom(){
doClass();
return classRoom;
}
// 编译报错:Error:(35, 9) java: 无法从静态上下文中引用非静态 方法 doClass()

3.静态方法是无法被重写的,这就无法形成多态,以及接口的使用。

**注意:**静态成员变量一般不会放在构造方法中来初始化,构造方法中初始化的是与对象相关的实例属性 静态成员变量的初始化分为两种:就地初始化静态代码块初始化

就地初始化:

就地初始化指的是:在定义时直接给出初始值。

静态代码块初始化

那什么是代码块呢?下面主要对静态代码块和匿名代码块进行介绍:

静态代码块

使用static定义的代码块称为静态代码块。一般用于初始化静态成员变量。

匿名代码块:

主要用于初始化普通的成员变量

下面看下两种代码块的实现语法及执行顺序:

代码举例:

public class Demo01 {
    //匿名代码块
    {
        System.out.println("执行匿名代码块");
    }
    //静态代码块
    static {
        System.out.println("执行静态代码块");
    }
    //构造方法
    public  Demo01() {
        System.out.println( "执行构造方法");
    }

    public static void main(String[] args) {
        Demo01 demo1 = new Demo01();
        System.out.println("=========");
        //第二次 静态方法不执行
        Demo01 demo2 = new Demo01();
    }
}

执行结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CobItu31-1648994409858)(杨克鸿(3) (2)].assets/image-20220328140619089.png)

执行顺序:

1、静态代码块 :和类一起加载,所以第一个执行 且静态代码块只执行一次

2、匿名代码块 :和对象一起加载,所以第二个执行

3、构造方法 :对象执行时,所以最后一个执行

三、new 关键字

使用new关键字创建对象的时候,除了给创建的对象分配内存空间外,还会进行默认的初始化,以及对类中的 构造器 调用。

四、构造器

特点: 1. 构造器的方法名必须和类名相同

​ 2. 没有返回类型

作用 :1 . 使用 new 关键字,必须要有构造器。

​ 2. 用来初始化值

注意 :1. 所有类中默认会存在一个 无参构造器

​ 2. 在定义一个有参构造后,默认的无参构造会失效,如果要使用无参构造。此时应该 显示的 自定义一个无参构造,否则会 报错。

代码举例:

public class Person {

    String name;
    int age;
    public void eat() {
        System.out.println(this.name + "正在吃饭");
    }
    public void sleep() {
        System.out.println(name + "正在睡觉");
    }
    //自定义的有参构造器
    public Person(String name) {
        this.name = name;
    }
    // 显示的 自定义的无参构造器
    public Person() {
    }
}

快捷键: IDEA 中生成 构造器的快捷键 Alt + insert 键

五、封装
(一) 作用

1、提高程序的安全性,保护数据

2、隐藏代码的实现细节 (封装后会隐藏代码实现功能的细节)

3、统一接口 (通过get set 统一接口)

4、增强系统的可维护性

(二)属性私有 及 get /set 的使用

​ 通常 类 中的属性我们将其 设置为 private 来防止外部类调用修改该类中该属性的值。 但 设置为 private 后,外部类创建对象无法 对该对象的属性进行赋值。此时可用 get /set 方法。

代码举例:

public class Person {

    private String name;
    private int age;
    private int high;

   //设置 属性name的 set方法,用来对该对象中的 name属性 存放数据
    public void setName(String s) {
        this.name = s;
    }
    
    //设置 属性name的 get方法,用来取出 该对象中的 name属性 
    public String getName() {
        return this.name;
    }
}
 public static void main(String[] args) {
        // 先 new 一个对象
        Person per = new Person();
     	通过 对象 per 中的set ,get 方法 存放和读取数据
        per.setName("大白");
        System.out.println(per.getName());
    }
六、继承
(一) 使用

子类通过 extends 关键字能够 继承父类的 方法和 成员变量 (非private 修饰)

public class Person {
    String name;
    private int age;
    private int high;
    public void eat() {
        System.out.println( this.name + "正在吃饭");
    }
}
public class Student extends Person{
    
}
//Student 继承了 Person 类的属性和方法

1、子类能够继承 父类中的所有成员变量和方法,只是对于父类中的 private修饰的方法和成员变量由于访问权限的问题,不能直接在子类中访问。

2、所有的类都默认直接或者间接继承了Object 类

3、Java 中只有单继承没有多继承 (一个儿子只能有一个亲生父亲,一个父亲可以有多个儿子)

(二) super 关键字

在子类中使用super 可以调用 父类中的 属性和方法(非私有)

//在子类中使用 super.属性名/方法名 的形式调用父类中的 属性或者方法 (非私有)
//子类
public class Student extends Person{
        protected String name = "大白";
        public void test1( String name) {
                System.out.println( this.name);//大白
                System.out.println(name);  //大黄
                super.setName("哈士奇");  //使用super调用 父类中的 setName 方法,给父类中的											name赋值
                System.out.println( super.getName());//哈士奇  使用super调用 父类中的 															  gettName 方法,获得父类中的name
        }
}
//父类
public class Person {
    private String name;

   public void eat () {
       System.out.println("吃饭");
   }
   public void sleep () {
       System.out.println("睡觉");
   }

   public void setName (String s) {
       this.name = s;
   }
  public String getName () {
      return this.name;
  }
  
}
//主函数(启动类)
public class Start {
    public static void main(String[] args) {
        Student stu = new Student();
        stu.test1( "大黄");
    }
}

上述代码中,在主函数 中new了一个Student 类的对象 stu。而后通过 stu对象调用子类中的 test1 方法,在test 1方法中分别打印了 子类中的名字 :“大白” 。 通过super 调用 父类中的方法,对父类中的name 进行存取,而后进行打印 。

注意:

1、super 在调用父类中的方法或属性时,会先调用 父类中的无参构造。

启动类
public class Start {
    public static void main(String[] args) {
        Student stu = new Student();
    }
}
子类
public class Student extends Person {
        public Student() {
                System.out.println("子类无参构造");
        }
}
父类
public class Person {
    public Person() {
        System.out.println("父类无参构造");
    }
}

此处代码执行结果
在这里插入图片描述

可以看到,父类的无参构造也被调用了。

所以这说明 在子类中的第一行代码其实隐藏了 一行调用父类无参构造的代码

子类
public class Student extends Person {
        public Student() {
        	//隐藏代码(调用父类的构造器)
        	super(); //如果将此代码显示出来,则此代码必须放在第一行
            System.out.println("子类无参构造");
        }
}

注意点:

1、super 调用父类的构造方法,必须在构造方法的第一个

2、super 必须只能出现在子类的方法或者构造方法中!(只能在继承条件下使用)

3、super 和 this 不能同时调用构造方法!( 因为 this() ,super() 调用构造方法都必须放在第一行)

4、super 和 this 不能在静态方法中使用

super 和 this 的比较

1、代表的对象不同

​ this : 调用的 是自身所在的对象 (指向当前对象的一个引用)

​ super :调用的是父类的对象

2、前提不同

​ this : 没有继承也可以使用

​ super : 只能在继承条件下使用

3、构造方法不同

​ this( ) : 调用的本类的构造

​ super( ) :调用父类的构造

(三) 方法的重写

重写: 必须要有继承关系,子类重写父类的方法。

//启动类
public class Start {
    public static void main(String[] args) {
        Person stu =  new Person();
        stu.test1();
        //子类中重写 test1 方法,此处父类的引用指向了子类
        Person per = new Student();
        per.test1();
    }
}
//父类
public class Person {
    public   void test1() {
        System.out.println("父类执行");
    }
}
//子类
public class Student extends Person {
  //重写的实现 快捷键: Alt + insert  选择 Override 自动生成重写
        public void test1() {
                System.out.println("子类执行");
        }
}

注意点:

1、子类中的 重写的方法与父类中的 方法名 参数列表* 必须要一致,方法体不同(两者只是 方法的实现方式不同)

2、修饰符 :范围可以扩大但不能缩小 :

父类中的 default 、 protected 、public修饰的方法,可以被重写为范围更大 方法

3、 private 修饰的方法不能被重写 ,只能重写非静态方法

4、 子类重写的 方法会影响父类中的方法。在动态调用时会调用子类中重写的方法。

5、抛出异常:范围,可以被缩小,但不能扩大。

为什么要有重写 ?

原因:父类的功能,子类不一定需要,或者不一定满足

七、多态

(一)概念

多态的概念:通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同 的状 态。

总的来说:同一件事情,发生在不同对象身上,就会产生不同的结果。

(二)实现条件

Java中要实现多态,必须满足以下条件:

1、 必须在继承关系下,发生向上转型

2、子类必须重写父类中方法

3、通过父类的引用调用重写方法

1、使用父类类型的引用指向子类的对象;
2、该引用只能调用父类中定义的方法和变量;
3、如果子类中重写了父类中的一个方法,那么在调用这个方法的时候,将会调用子类中的这个方法;(此过程称为 :动态连接 或 动态调用)
4、变量不能被重写(覆盖),”重写“的概念只针对方法,如果在子类中”重写“了父类中的变量,那么在编译时会报错。

//启动类
public class Start {
    public static void main(String[] args) {
        //创建的父类类型的对象  (正常)
        Father f1 = new Father();
        f1.fun1();  //打印 父类执行
        
        //创建的 父类类型的对象,指向子类 (多态)
         Father f2 = new Son();
         f2.fun1(); //发生动态调用,打印子类执行
         f2.fun2(); //父类中fun2 没有被重写,此处打印 父类执行2
        
        //创建的 子类类型的对象
        Son son = new Son();
        son.fun1(); //打印 子类执行
        son.fun2(); //父类中fun2 没有被重写,此处打印 父类执行2
    }
}
//父类
public class Father {
    public void fun1 () {
        System.out.println( "父类执行");
    }
     public  void fun2() {
        System.out.println( "父类执行2");
    }
}
//子类
public class Son extends Father{
    @Override (重写 父类中的fun1)
    public void fun1() {
        System.out.println("子类执行");
    }
}

多态:

八、抽象类
(一) 概念

在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是 抽象类

(二)使用

代码举例:创建一个抽象类,并使永一个子类继承,实现其中的抽象方法

// 关键字 abstract 作用在类上,这个类就是一个抽象类
public abstract class Person {
    //abstract 作用在方法上,这个方法就是一个抽象方法: 抽象方法只有方法的名字没有方法的实现
    public abstract void eat();
}

//创建一个 Person 的子类
public class Student extends Person {
    //此处必须重写实现 父类中的 eat方法,否则报错。 因为:抽象类的子类必须要实现父类中的抽象方法,除非 子类也是一个抽象类
    @Override
    public void eat() {
        System.out.println( "吃饭");
    }
}
(三)特点

1、 抽象类中可以写普通方法,但抽象方法必须在抽象类中

2、抽象类是无法进行实例化创建对象的,只能去实例化它的子类来创建对象。

代码举例:

//抽象类中可以写普通方法
public abstract class Person {
    public abstract void eat();
    //创建普通方法,此处不会报错
     public void sleep() {
        System.out.println("睡觉");
    }
}

//抽象方法写在非抽象类中
public class Teacher {
    public abstract void sleep() {    //报错!!
        System.out.println("睡觉");
    }
}

在主方法中new一个抽象类是不可行的 。
在这里插入图片描述

(四)作用

抽象类这么抽象,那它的具体作用是什么呢?

其实利用抽象类,有利于提高代码的开发效率。

如: 创建一个游戏角色,其特性比较复杂,反复创建会比较麻烦,我们可以利用 抽象类 抽象其一些公有的特性,而后我们每创建一个角色,就可以去写一个子类 继承这个抽象类,再去重写一些其中方法,改掉一些不必要方法。可以提高开发效率,和可拓展性。

九、接口
(一)概念

接口就是公共的行为规范标准,大家在实现时,只要符合规范标准,就可以通用。

在Java中,接口可以看成是:多个类的公共规范,是一种引用数据类型。

(二)语法规则

接口的定义格式与定义类的格式基本相同,将class关键字换成 interface 关键字,就定义了一个接口。

代码举例:

//定义一个接口
public interface Demo02 {
    //定义抽象方法
    //接口中的所有定义都是抽象的,其中方法默认形式为 public abstracter
    public abstract void add();
    public abstract void delete();
}

ps: 接口中可以定义常量,其类型默认为 public static final 。但通常不这么用。

​ 指向的灰色部分为默认的类型,可以不用写
在这里插入图片描述

(三) 接口的使用

接口不能直接使用,必须要有一个"实现类"来"实现"该接口,实现接口中的所有抽象方法。(接口中方法都必须要被重写)

通过 implements 关键字

代码举例:

//定义一个接口
public interface Demo02 {
    //定义抽象方法
    public abstract void add();
    public abstract void delete();
}

//通过方法的重写实现接口中的方法
public class UserServiceImpl implements Demo02 {
    @Override
    public void add() {
        System.out.println("添加");
    }

    @Override
    public void delete() {
        System.out.println("删除");
    }

}
(四)接口可以实现多继承

在Java中,类和类之间是单继承的,一个类只能有一个父类,即Java中不支持多继承,但是一个类可以实现多个接 口。

//定义第一个接口
public interface Demo01 {
    public abstract void add();
    public abstract void delete();
}

//定义第二个接口
public interface Demo02 {
    void updata();
    void query();
}

//通过方法的重写实现接口1和接口2中的方法
public class UserServiceImpl implements Demo01,Demo02 {
    @Override
    public void add() {
        System.out.println("添加");
    }

    @Override
    public void delete() {
        System.out.println("删除");
    }

    @Override
    public void updata() {
        System.out.println("更新");
    }

    @Override
    public void query() {
        System.out.println("修改");
    }
}

注意:一个类实现多个接口时,每个接口中的抽象方法都要实现,否则类必须设置为抽象类。

即Java中不支持多继承,但是一个类可以实现多个接 口。

//定义第一个接口
public interface Demo01 {
    public abstract void add();
    public abstract void delete();
}

//定义第二个接口
public interface Demo02 {
    void updata();
    void query();
}

//通过方法的重写实现接口1和接口2中的方法
public class UserServiceImpl implements Demo01,Demo02 {
    @Override
    public void add() {
        System.out.println("添加");
    }

    @Override
    public void delete() {
        System.out.println("删除");
    }

    @Override
    public void updata() {
        System.out.println("更新");
    }

    @Override
    public void query() {
        System.out.println("修改");
    }
}

注意:一个类实现多个接口时,每个接口中的抽象方法都要实现,否则类必须设置为抽象类。

  • 7
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值