Java自学(六、Java面向对象编程(OOP)详解)

记录自己在狂神说java中的学习情况,文章里有自己学习的理解和扩展,新手难免有理解偏差或者错误,恳请大佬指正。

Java面向对象编程(OOP)

Java的核心思想就是OOP(面向对象编程)

初识面向对象

面向过程思想

  • 步骤清晰简单,第一步做什么,第二步做什么,就相当于是流程步骤
  • 面向过程适合处理一些较为简单的问题

面向对象思想

  • 物以类聚,分类的思维模式,思考问题首先会解决问题需要哪些分类,然后对这些分类进行单独思考。最后,才对某个分类下的细节进行面向过程的思索。
  • 面向对象适合处理复杂的问题,适合处理需要多人协作的问题

对于描述的复杂的事事物,为了从宏观上把握、从整体上合理分析,我们需要使用面向对象的思路来分析整个系统。但是,具体到微观操作,仍然需要面向过程的思路去处理

什么是面向对象?

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

面向对象编程的本质就是:以类的方式组织代码,以对象的方式组织(封装)数据。

类是抽象的

三大特性:

  • 封装:将属性私有化,提供公共的访问方法
  • 继承:子类拥有父类的特征和行为,使得子类对象也具有父类的实例域和方法。
  • 多态:多态性是对象多种表现形式的体现。重写和继承实现多态。同一个Animal类可以是dog,cat,fish。

从认识论角度考虑是先有对象后有类。对象,是具体的事物。类是抽象的,是对对象的抽象。

从代码运行角度考虑是先有类后有对象。类是对象的模板。

方法回顾和加深

回顾方法的定义

  • 修饰符
  • 返回类型
package com.hbq.oop;
//类
public class Methods {
    //main方法
    public static void main(String[] args) {

    }
    // 修饰符(public) 返回值类型(String) 方法名(sayHello){
    //        // 方法体;
    //        return 返回值;
    // }
    public String sayHello() {
        return "hello,world";
    }
    public int max(int a, int b) {
        return a > b ? a : b;
    }
}
  • break和return的区别

    • break可以跳出switch或者结束当前最近一层的循环。
    • 而return则代表方法的结束,返回return后面的结果,return后面的值的类型一定要和方法定义的返回值类型相同。
  • 方法名:注意规范,见名知意。

  • 参数列表:(参数类型 参数名)还有可变长参数(int… a)要放到最后

  • 异常抛出

public void readFile(String file) throws IOException{
    //方法体
}

方法的调用

  • 静态方法
  • 非静态方法
// Student类
package com.hbq.oop;
public class Student {
    //非静态方法
    public void sayNoStatic() {
        System.out.println("学生说非静态话了");
    }

    //静态方法
    public static void sayStatic() {
        System.out.println("学生说静态话了");
    }
}
package com.hbq.oop;
public class MethodUse {
    public static void main(String[] args) {
        // 静态方法 static 调用方法
        Student.sayStatic();
        // 非静态方法
        Student student = new Student();
        student.sayNoStatic();
    }
    // 当前类内的静态和非静态方法的调用情况
    // a 和 b 都是非静态方法的时候,可以互相调用
    public void a() {
        System.out.println("a");
        b();
    }
    public void b() {
        System.out.println("b");
        a();
    }
    // aa和bb都是静态方法的时候,也可以互相调用
    public static void aa() {
        System.out.println("a");
        bb();
    }
    public static void bb() {
        System.out.println("b");
        aa();
    }
    // 当aaa和bbb一方是静态方法一方不是静态方法时,静态方不能调用非静态方
    // 因为静态方法是和类一起加载的
    // 而非静态方法是随着类实例化后才存在的
    // 所以静态方不能去调用一个还不存在的方法
    // 但是非静态方法却可以调用静态方法
    public static void aaa() {
        System.out.println("a");
//        bbb();
    }
    public void bbb() {
        System.out.println("b");
        aaa();
    }
}

可以看一张表格:

静态非静态
static关键字不需要static关键字
使用类名调用使用实例对象调用
在静态方法中,可以访问静态成员在实例方法中,可以直接访问静态成员
在静态方法中,不可以直接访问实例成员在实例方法中,可以直接访问实例成员
调用前初始化(和类一起)实例对象时初始化
共享代码段,可能存在并发问题不共享代码段,不会有线程安全问题
静态方法和成员自创建后就始终使用同一块内存不同实例会创建不同的内存
  • 形参和实参
package com.hbq.oop;
public class ParameterUse {
    public static void main(String[] args) {
        // 调用的时候传入实参,然后实参赋值给形参,注意实参和形参的类型要对应
        int add = ParameterUse.add(1, 2);
        System.out.println(add);
    }
    //这个a和b暂时都是不存在的,所以现在是形式上的参数,形参
    public static int add(int a, int b) {
        return a + b;
    }
}
  • 值传递和引用传递

首先Java都是值传递。引用只不过是把对象的地址的值传递了。

package com.hbq.oop;
public class ValueSend {
    public static void main(String[] args) {
        //值传递
        int a = 1;
        System.out.println(a);//1
        change(a);
        System.out.println(a);//1
        //引用传递:对象(本质还是值传递)
        Person person = new Person();
        System.out.println(person); //com.hbq.oop.Person@16d3586
        System.out.println(person.name); //null
        change(person);
        System.out.println(person.name); //我是谁
    }
    public static void change(int a) {
        a = 10; //只是走了个形参,实参还是1
    }
    public static void change(Person p) {
        // p是一个对象,指向Person person=new Person();这是一个具体的人,可以改变属性。
        // 传递的是person的地址
        System.out.println(p); //com.hbq.oop.Person@16d3586
        p.name = "我是谁";
    }
}
//定义了一个Person类,有一个属性:name
class Person {
    String name;
}
  • this关键字

对象的创建分析

类与对象的关系

类是一种抽象的数据类型,它是对某一类事物整体描述/定义,但是并不能代表某一个具体的事物。类就像:

  • 动物、植物、手机、电脑
  • Animal类、Plant类、Phone类、Computer类

对象是抽象概念的具体实例,比如刚刚上面的举例中

  • 我家的柴犬就是动物类的一个具体实例,我养的水仙花就是植物类的一个具体实例

创建与初始化对象

使用new关键字创建对象

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

package com.hbq.oop;
//学生类
public class Students {
    //属性:字段
    String name;
    int age;
    //方法
    public void study() {
        System.out.println(name + "学生在学习");
    }
}
package com.hbq.oop;
// 一个项目应该只存在一个main方法
public class Application {
    public static void main(String[] args) {
        // 类:抽象的,需要实例化
        // 类实例化后会返回一个自己的对象
        // student对象就是一个Student类的具体实例
        Students xiaohong = new Students();
        Students xiaoming = new Students();
        xiaoming.name = "小明";
        xiaoming.age = 3;
        System.out.println(xiaoming.name);
        System.out.println(xiaoming.age);
        xiaohong.name = "小红";
        xiaohong.age = 3;
        System.out.println(xiaohong.name);
        System.out.println(xiaohong.age);
        xiaoming.study();
        xiaohong.study();
    }
}

类中的构造器也称为构造方法,是在进行创建对象的时候必须调用的。并且构造器有以下两个特点:

  1. 必须和类的名字相同
  2. 必须没有返回类型,也不能写void

构造器必须掌握

package com.hbq.oop;
//Persons类
public class Persons {
    //一个类即使什么都不写,都会存在一个构造方法
    //构造方法必须和类名相同,而且不能有返回值,void也不行
    String name;
    // 实例化使用new关键字,本质上是在调用构造器用来初始化
    public Persons() {//这就是一个无参构造器
        //可以实例化的时候给一些初始值
        name = "我是谁";
    }
    // 一旦定义了有参构造,无参构造就必须显式定义,否则就不能new Persons()。因为自己不写构造器的时候,会自动给一个无参的构造器,写了以后就不会自动了,所以要自己加上。
    public Persons(String name) {
        this.name = name;
    }
}
package com.hbq.oop;

// 一个项目应该只存在一个main方法
public class Application {
    public static void main(String[] args) {
        // 类:抽象的,需要实例化
        // 类实例化后会返回一个自己的对象
        // 这里的Persons()其实就是Persons类的构造方法,就算我们没有写这个构造方法,也会有默认的构造方法
        Persons p = new Persons("小红");
        System.out.println(p.name);
    }
}

构造器:

  1. 和类名相同
  2. 没有返回值

作用:

  1. new本质上在调用构造方法
  2. 初始化对象的值

注意点:

  1. 定义有参构造以后,如果想使用无参构造,显示的定义一个无参的构造器

下面来看例子看看具体实例化在内存中的变化:

package com.hbq.oop;
//Pet类
public class Pet {
    String name;
    int age;
    public void shout() {
        System.out.println("叫了一声");
    }
}
package com.hbq.oop;
public class Application {
    //①首先main变量进入堆,然后Application进入堆中的方法区,里面有常量和方法
    public static void main(String[] args) {
        //②然后开始实例化Pet类,实例对象名为dog,将dog变量压入栈,然后Pet类在方法区中包含了一些常量和方法以及属性。并且通过new Pet()指令,在堆中开拓一块新的内存空间(假设为0x0001),然后dog变量里存的就是这个地址0x0001。
        Pet dog = new Pet(); 
        //③开始对实例对象进行修改,这里的修改是在堆中的0x0001开始的那块内存区域里修改的
        dog.name = "旺财";
        dog.age = 3;
        dog.shout();
        System.out.println(dog.name);
        System.out.println(dog.age);
		//④同理再实例化一个对象命名为cat也是一样的道理
        Pet cat = new Pet();
        //⑤更新cat指向的内存区域里的值
        cat.name = "喵喵";
        cat.age = 2;
        cat.shout();
    }
}

在这里插入图片描述

简单小结

有了这个图以后,可以对类和对象进行一个简单的总结

  1. 类与对象的关系

    类是一个模板,是抽象的。对象是一个具体的实例。

  2. 方法

    方法的定义和调用要会使用

  3. 对应的引用

    引用类型(除了基本类型以外的)和基本类型(八大基本类型:int,double,float,char,boolean,long,short,byte)

    引用类型:对象是通过引用来操作的,也就是上面图中的栈—》堆。变量在栈里存储了堆中的地址,实际对象本身上是在堆中存储的。

  4. 属性,也叫字段,也叫成员变量

    默认初始化:

    • 数字默认0或0.0
    • char:u0000
    • boolean:false
    • 引用:null

    赋值通式

    修饰符 属性类型 属性名 = 属性值;

  5. 对象的创建和使用

    必须使用new关键字创建对象,构造器也要。Person p = new Person();

    对象的属性 p.name

    对象的方法 p.sleep()

  6. 类:

    类里就两种东西

    静态的属性:属性

    动态的行为:方法

面向对象三大特性

封装

封装通俗来说就是该露的露,该藏的藏

​ 程序设计要求“高内聚,低耦合”。高内聚就是类的内部数据操作细节自己完成,不允许外部干涉;低耦合就是仅暴露少量的方法给外部使用。比如电视机,只给用户换台按钮,调音量按钮,通过按钮去操作,而不需要知道电视机的具体细节。

封装(数据的隐藏)

​ 通常,应该禁止直接访问一个对象中数据的实际表示,而应该通过操作接口来访问,这称为信息隐藏。

记住一句话:属性私有,操作看get/set。

package com.hbq.oop.encapsulation;
//Student类 private:私有
public class Student {
    // 属性私有
    // 名字
    private String name;
    // 学号
    private int id;
    // 性别
    private char sex;
    // 年龄
    private int age;
    // 提供一些可以操作这个属性的方法
    // 提供一些public的get和set方法
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public char getSex() {
        return sex;
    }
    public void setSex(char sex) {
        this.sex = sex;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) { //在设置时加安全性检查
        if (age < 120 && age > 0)
            this.age = age;
        else this.age = 0;
    }
}
package com.hbq.oop.encapsulation;
public class Encapsulation {
    public static void main(String[] args) {
        Student s1 = new Student();
        //s1.name //不能直接用了,会标红
        s1.setName("张三");
        System.out.println(s1.getName());
        s1.setAge(99999);//不合法数据
        System.out.println(s1.getAge());
    }
}

封装的好处:

  1. 提高程序的安全性,保护数据
  2. 隐藏代码的实现细节
  3. 统一接口
  4. 系统可维护性提高

继承

继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模,比如动物其实又分哺乳动物、爬行动物等等,哺乳动物又能继续细分。(类是对对象的抽象,父类又是对一批类的抽象)

extands的意思是扩展。子类时父类的扩展。

注意!Java中类只有单继承,没有多继承!但是可以有多重继承!

在这里插入图片描述

继承是类和类之间的一种关系,除此之外,类和类之间的关系还有依赖、组合、聚合

继承关系的两个类,一个为子类(派生类),一个为父类(基类)。子类继承父类,使用关键字extends来表示

子类和父类之间,从意义上讲应该具有“is a”的关系,比如哺乳动物 is a 动物

在Java中,所有的类都默认直接或者间接继承Object

final 关键字声明类可以把类定义为不能继承的,即最终类;或者用于修饰方法,该方法不能被子类重写

继承的特性:

  • 子类拥有父类非 private 的属性、方法。
  • 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
  • 子类可以用自己的方式实现父类的方法。
  • Java 的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如 B 类继承 A 类,C 类继承 B 类,所以按照关系就是 B 类是 C 类的父类,A 类是 B 类的父类,这是 Java 继承区别于 C++ 继承的一个特性。
  • 提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系越紧密,代码独立性越差)。

关于Java的访问权限,一共有四种:

修饰符同一个类同一个包子类所有类
public允许访问允许访问允许访问允许访问
protected允许访问允许访问允许访问
default(jdk8引入的)允许访问允许访问
private允许访问

下面来看一些实例。

package com.hbq.oop.inheritance;
//Person 人 :基类 父类
public class Person {
    private int money = 10_000_000; //钱是私有的
    public void say() {
        System.out.println("说了一句话");
    }
    public int getMoney() {
        return money;
    }
    public void setMoney(int money) {
        if (money >= 0)
            this.money = money;
    }
}
package com.hbq.oop.inheritance;
// 学生 is a 人: 派生类 子类
public class Student extends Person {}
package com.hbq.oop.inheritance;
// 老师 is a 人 :派生类 子类
public class Teacher extends Person{}
package com.hbq.oop.inheritance;
public class Application {
    public static void main(String[] args) {
        Student s1 = new Student();
        Teacher t1 = new Teacher();
        s1.say(); // 可以使用父类的public方法
        System.out.println(t1.getMoney()); // 可以使用父类的public方法来获得父类的private属性
    }
}

一些小知识。

  • object类
    • 祖先类,Java中所有的类都间接或者间接继承Object
  • super
package com.hbq.oop.inheritance;
//Person 人 :基类 父类
public class Person {
    public Person(){
    }
    public Person(String name,int age){
        System.out.println("Person无参执行了");
    }
    protected String name="张三";
    public void print(){
        System.out.println("person");
    }
    private void privatePrint(){
        System.out.println("子类用super也不能调用");
    }
}
package com.hbq.oop.inheritance;
// 学生 is a 人: 派生类 子类
public class Student extends Person {
    public Student() {
        // 隐藏代码:调用了父类的无参构造
        super("name", 1); // 调用父类的构造器,要在子类构造器的第一行
        System.out.println("student无参执行了");
    }
    private String name = "李四";
    public void test(String name) {
        System.out.println(name); //形参的name
        System.out.println(this.name); //当前类里的name
        System.out.println(super.name); //父类里的name
    }
    public void print() {
        System.out.println("student");
    }
    public void test1() {
        print(); //当前类的print()
        this.print(); //当前类的print()
        super.print(); //父类的print()
        // super.privatePrint();// 父类的私有方法无法被调用
    }
}
package com.hbq.oop.inheritance;
public class Application {
    // 主函数
    public static void main(String[] args) {
        Student s2 = new Student();
        s2.test("王麻子");
        s2.test1();
    }
}

​ super的使用的注意点:

​ 1.super调用父类的构造方法,必须在子类构造方法的第一行

​ 2.super只能出现在子类的方法或者构造方法中

​ 3.super和this不能同时调用构造方法!因为都要在第一行,矛盾了

​ 和this的对比:

​ 1.代表的对象不同:this代表调用者这个对象,super代表父类对象的应用

​ 2.使用前提不同:this没有继承也可以使用,super必须有继承关系才可以使用

​ 3.构造方法:this();本类的构造方法,super();父类的构造方法

  • 方法重写

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

    ​ 1.方法名必须相同

    ​ 2.参数列表必须相同

    ​ 3.修饰符:范围可以扩大但是不能缩小。父类的private子类可以public或者protected。public>protected>default>private

    ​ 4.抛出的异常:范围可以被缩小但不能扩大。Exception–>ClassNotFoundException

    重写,子类的方法和父类的必须要一致

    为什么需要重写?子类不一定需要父类的功能,或者父类功能不够

具体例子

package com.hbq.oop.Polymorphism;
// 重写都是方法的重写,和属性无关
// B类,父类
public class B {
    public static void test(){
        System.out.println("B=>test()");
    }
    public void test1(){
        System.out.println("B=>test1()");
    }
}
package com.hbq.oop.Polymorphism;
// A类,子类
public class A extends B {
    public static void test(){
        System.out.println("A=>test()");
    }
    // 这就是重写
    @Override
    public void test1(){
        System.out.println("A=>test1()");
    }
}
package com.hbq.oop.Polymorphism;
// 主函数类
public class Application {
    // 静态的和非静态的方法区别很大!
    // 静态方法是类的方法,在有类的时候就创建了,不会重写
    // 非静态方法是对象的方法,要等类被实例化的时候才有,会被重写
    public static void main(String[] args) {
        // 方法的调用只和左边,定义的数据类型有关
        A a = new A();
        a.test(); //A
        a.test1(); //A
        // 父类的引用指向了子类
        B b = new A();
        b.test(); //B
        b.test1(); //A
    }
}

多态

即同一方法可以根据发送对象的不同而采用多种不同的行为方式。

可以实现动态编译:可以使得程序的可扩展性更强。

一个对象的实际类型是确定的,但可以指向对象的引用类型有很多(有引用的类)

多态存在的条件:

  • 有继承关系
  • 子类重写父类方法
  • 父类引用指向子类对象

注意:

1.多态是方法的多态,属性没有多态性

2.父类和子类。 否则有类型转化异常。比如String s= new Student();,ClassCastException

3.存在条件:继承关系,方法需要重写,父类引用指向子类对象 Father f1= new Son();

static方法不能被重写,属于类,不属于实例,final是常量,不能改变,所以也不能重写 ,private方法是私有的,也不能重写。构造器也不能重写。

instanceof,引用类型转化

package com.hbq.oop.instanceofuse;
//Person类
public class Person {
    public void run(){
        System.out.println("run");
    }
}
package com.hbq.oop.instanceofuse;
// Student类
public class Student extends Person {
    public void go() {
        System.out.println("go");
    }
}
package com.hbq.oop.instanceofuse;
//Teacher类
public class Teacher extends Person {
}
package com.hbq.oop.instanceofuse;
public class Application {
    public static void main(String[] args) {
        // object >派生 String
        // object >派生 Person >派生 Teacher
        // object >派生 Person >派生 Student
        // instanceof 一定要有父子关系才能比较,否则编译就报错,X instanceof Y。
        // instanceof是Java中的二元运算符,左边是对象,右边是类;当对象是右边类或子类所创建对象时,返回true;否则,返回false。
        // 类的实例包含本身的实例,以及所有直接或间接子类的实例
        // instanceof左边显式声明的类型与右边操作元必须是同种类或存在继承关系,也就是说需要位于同一个继承树,否则会编译错误
        // null用instanceof跟任何类型比较时都是false
        /*
         *                   object
         *                   /    \
         *               String  Person
         *                        /   \
         *                   Teacher Student
         * 在从根到叶子节点的一条路径上的结点才能使用instanceof不报错。
         * instanceof的返回结果和new的对象有关,new Student()就表示这个对象是Student的,只有Student到根的那条路径上的类才返回true
         * 编译报错看左边,运行结果看右边
         * */
        Object ob = new com.hbq.oop.Polymorphism.Student();
        System.out.println(ob instanceof Student); //true
        System.out.println(ob instanceof Person); //true
        System.out.println(ob instanceof Object); //true
        System.out.println(ob instanceof Teacher); //false
        System.out.println(ob instanceof String); //false
        
        Person pe = new Student();
        System.out.println(pe instanceof Student); //true
        System.out.println(pe instanceof Person); //true
        System.out.println(pe instanceof Object); //true
        System.out.println(pe instanceof Teacher); //false
//        System.out.println(pe instanceof String); //编译报错
        
        Student st = new Student();
        System.out.println(st instanceof Student); //true
        System.out.println(st instanceof Person); //true
        System.out.println(st instanceof Object); //true
//        System.out.println(st instanceof Teacher); //编译报错
//        System.out.println(st instanceof String); //编译报错

        // 类型之间的转换。 父高      子低,低转高自动转(就和基本类型里低精度转高精度一样),高转低要强转
        // 高                  低        低转高自动转
        Person student = new Student();
//        student.go();//标红,因为Person类里没有go方法
        // 将Person类型转化成Student对象,高转低强制转化,有go方法了
        ((Student) student).go();
        // 子类转化为父类可能会丢失自己本来的一些方法
        /*
        1.因为父类引用可以指向子类对象,所以子类转父类不需要强转
        2.但是子类引用不可以指向父类对象,所以父类转子类需要强转
        3.把子类转化为父类,向上转型,注意可能会丢失没有重写的子类方法
        4.把父类转化为子类,向下转型,要强制转化
        5.方便方法的调用,减少重复的代码,简洁
        * */
    }
}

static关键字详解

package com.hbq.oop.staticclass;
// static :
public class Student {
    private static int age; // 静态的变量
    private double score; // 非静态变量
    public void run(){
        System.out.println("run");
    }

    public static void go(){
        System.out.println("go");
    }
    public static void main(String[] args) {
        Student st = new Student();
        System.out.println(st.score);
        System.out.println(st.age); //可以通过对象来调用
        System.out.println(Student.age); //也可以通过类来调用
//        System.out.println(Student.run());//标红,非静态方法要实例化才能调用
        Student.go();// 静态方法可以通过类.来调用,甚至在类里可以直接调用
        go();// 类里直接调用静态方法
    }
}
package com.hbq.oop.staticclass;
//静态导入包,如果没有static这里random会标红
import static java.lang.Math.random;
import static java.lang.Math.PI;
public class Person {
    {   //可以赋初始值
        //代码块(匿名代码块)在实例化的时候执行(比构造器还早执行)
        System.out.println("匿名代码块");
    }
    static {
        //静态代码块 在类加载的时候就直接执行,全局执行一次
        System.out.println("静态代码块");
    }
    public Person() {
        System.out.println("构造方法");
    }
    public static void main(String[] args) {
        Person p = new Person();
        //输出
        //静态代码块
        //匿名代码块
        //构造方法
        System.out.println("==========================");
        Person p2= new Person();
        //输出
        //匿名代码块
        //构造方法
    }
}

抽象类

抽象类:abstract修饰符可以用来修饰方法也可以修饰类,如果修饰方法,那么该方法就是抽象方法,如果修饰类,那么该类就是抽象类。

抽象类中可以没有抽象方法,但是抽象方法的类一定要声明为抽象类。

抽象类,不能使用new关键字来创建对象,它是用来让子类继承的。

抽象方法,只有方法的声明,没有方法的实现,它是用来让子类实现的。

子类继承抽象类,那么就必须要实现抽象类没有实现的抽象方法,否则该子类也要声明为抽象类。

实战!

package com.hbq.oop.AbstractAction;
// abstract 抽象类
public abstract class abstractAction {
    // 对方法进行约束,然后子类再实现
    // abstract 抽象方法,只有方法定义,没有方法的实现
    public abstract void doSomething();
    // 1.不能new这个抽象类,只能靠子类去实现它,只是个约束
    // 2.抽象类中可以写普通的方法
    // 3.抽象方法必须在抽象类中
    // 抽象类其实就是一个约束
    public void run() {
        System.out.println("run");
    }
    //思考题?
    // 既然不能new,那它存在构造器吗?
    // 答:抽象类也是存在构造器的,因为非抽象子类实例化的时候会先调用父类的构造器,所以抽象类的构造器只能在子类实例化的时候被调用。
    public abstractAction(){
        System.out.println("抽象类无参构造器");
    }
    public abstractAction(int i){
        System.out.println("抽象类有参构造器");
    }
    // 抽象类存在的意义? 抽象出公共的东西,提高程序的可扩展性,提高开发效率。
}
package com.hbq.oop.AbstractAction;
// 抽象类的所有方法,继承了它的子类,都必须要实现它的方法,除非子类也是抽象类
public class A extends abstractAction {
    @Override
    public void doSomething() {
    }

    public A() {
        System.out.println("子类无参构造器");
    }
    public A(int i) {
        super(i);
        System.out.println("子类有参构造器");
    }
    public static void main(String[] args) {
        A a = new A(); // 会先调用父类(也就是抽象类)的构造器,然后调用自己的构造器
        abstractAction b=new A(2); // 会先调用父类(也就是抽象类)的构造器,然后调用自己的构造器
    	//输出
        //抽象类无参构造器
		//子类无参构造器
		//抽象类有参构造器
		//子类有参构造器
    }
}

接口

Java中一些类的内容规则:

普通类:只有具体实现

抽象类:具体实现和规范(抽象方法)都可有

接口:只有规范(抽象方法),自己是无法写方法的!专业的抽象,实现约束和实现分离。面向接口编程!

接口就是规范,定义的是一组规则,体现了现实世界中的“如果你是。。。则必须能。。。”的思想。如果你是天使,则必须能飞。如果你是汽车,则必须能跑。

接口的本质是契约,就像人间的法律一样,制定好后大家都遵守

OO的精髓,是对对象的抽象,最能体现这一点的就是接口。为什么我们讨论设计模式都只针对具备了抽象能力的语言(比如C++、Java、C#等),就是因为设计模式所研究的,实际上就是如何合理的去抽象。

声明类的关键字是class,声明接口的关键字是interface!

Java中最重要的其实就是抽象的思维。

package com.hbq.oop.interfaces;
//interface 定义的关键字,接口都需要有实现类
public interface UserService {
    // 接口中所有定义的方法,都是抽象的,而且都是public的
    // 接口中所有定义的变量,都是static final常量,而且也都是public的
    int AGE = 99;
    void add(String name);
    void delete(String name);
    void update(String name);
    void query(String name);
//    protected void pp(); //protected会标红
//    private void pr(); //private会标红
}
package com.hbq.oop.interfaces;
public interface TimeService {
    void timer();
}
package com.hbq.oop.interfaces;
// 类可以实现接口, implements 接口
// 实现了接口的类,就需要重写接口中的方法
// 和继承不同,implements后面可以跟多个接口
// 所以单继承可以利用接口实现多继承
public class UserServiceImpl implements UserService, TimeService {

    @Override
    public void add(String name) {}

    @Override
    public void delete(String name) {}

    @Override
    public void update(String name) {}

    @Override
    public void query(String name) {}

    @Override
    public void timer() {}
}

接口的作用:

  1. 是一种约束
  2. 定义一些方法,让不同的人去实现,一个接口可能有N多种实现方式
  3. 方法都是public abstract的
  4. 变量都是public static final的
  5. 接口不能被实例化,!注意这里与抽象类的区别。接口没有构造方法!!!,而抽象类有!
  6. implements可以实现多个接口
  7. 必须要重写接口中的所有方法

内部类

内部类其实就是在一个类的内部再定义一个类,比如A类中定义一个B类,那么B类相对于A类来说就称为内部类,而A类相对B类来说就是外部类了。

成员内部类

语法:

修饰符 class 外部类名称{
	修饰符 class 内部类名称{
		//...
	}
	//...
}

静态内部类

语法:

修饰符 class 外部类名称{
	修饰符 static class 内部类名称{
		//...
	}
	//...
}

局部内部类

语法:

修饰符 class 外部类名称 {
    修饰符 返回值类型  外部类方法名称(参数列表) {
        class 局部内部类名称 {
            //...
        }
    }
}

匿名内部类

语法:

接口名称 对象名 = new 类名或者接口名称() {
    //重写所有抽象方法
};

实战例子

package com.hbq.oop.InternalClass;
// 外部类
public class OuterClass {
    private int id = 10;
    public void out() {
        System.out.println("这是外部类的方法");
        // 局部内部类
        class ZoneInner {
            public void in() {
                System.out.println("局部内部类作用域在方法里");
            }
        }
    }
    // 成员内部类
    public class InnerClass {
        public void in() {
            System.out.println("这是内部类的方法");
        }

        // 获得外部类的私有属性
        public void getID() {
            System.out.println(id);
        }

        public void out() {
            OuterClass.this.out();
        }
    }
    // 静态内部类
    public static class InnerClass2 {
        public void getID() {
            //System.out.println(id);//会标红,因为static在类加载就存在了,但是id不是静态成员,必须实例化才有,所以不存在
            System.out.println("静态内部类方法");
        }
    }
}
package com.hbq.oop.InternalClass;
public class Application {
    public static void main(String[] args) {
        // 通过new 关键字 new一个外部类
        OuterClass outer = new OuterClass();
        // 通过外部类来实例化内部类
        OuterClass.InnerClass inner = outer.new InnerClass(); //通过外部类的实例来实例化内部类
        inner.in();
        inner.getID();
        OuterClass.InnerClass2 inner2 = new OuterClass.InnerClass2(); //静态内部类要通过外部类的类名.静态内部类来实例化,而不是外部类的实例来定义
        //匿名内部类,没有名字初始化类
        new OuterClass().out();
//        new OuterClass.InnerClass().getID(); //会标红,非静态内部类不行,必须在对象里实例化
        new OuterClass.InnerClass2().getID();
        // 这里其实就是实现了这个UserService接口的类,但是这个类没有名字,为匿名内部类
        UserService userService = new UserService() {
            @Override
            public void hello() {
                System.out.println("hello");
            }
        };
        userService.hello();
    }
}
interface UserService {
    void hello();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值