面向对象

1.概念

  • Java的核心思想就是面向对象编程(OOP)其中OO是面向对象
  • 属性+方法=类
  • 面向对象编程的本质:以类的方式组织代码,以对象的组织(封装)具体数据。
  • 抽象:把共同点剥离出来组成类
  • 三大特性
  1. 封装
  2. 继承
  3. 多态
  • 对象是具体的,类是抽象的
  • 从代码角度考虑:先有类,再有对象。类是对象的模板
  • 使用new关键字创建的时候,除了分配内存空间之外,还会给创建好的对象进行默认初始化,以及对类中构造器的调用(构造器必须掌握)

2.简单的创建类及调用

package com.oop.demo02;

//学生类
public class Student {

//一个类只会有两种东西:属性和方法
    //属性:字段
    String name;
    int age;

    //方法
    public void study(){
        System.out.println(this.name+"在学习");//this代表当前类
        //可以引用当前类中的属性

    }
}
//比如person类 --->身高,体重,年龄等属性




/*
//测试类
 public static void main(String[] args) {

        //类:抽象的,需要实例化
        //类实例化后会返回一个自己的对象
        //xiaoming对象就是一个Student类的具体实例
        Student xiaoming = new Student();
        Student xh = new Student();

        xiaoming.name = "小明";
        xiaoming.age = 3;

        xh.name = "小红";
        xh.age = 5;

        System.out.println(xiaoming.name);
        System.out.println(xiaoming.age);

        System.out.println(xh.name);
        System.out.println(xh.age);
    }
 */
小明
3
小红
5

Process finished with exit code 0

3.有参无参构造器

  • 类中构造器也称为构造方法,是在进行创建对象的时候必须要调用的,并且构造器有以下两个特点
    ※ 必须和类的名字相同
    ※ 必须没有返回类型,也不能写void
  • 注意事项
  1. Alt+insert / Constructor自动生成构造器 (可以选无参和有参)
  2. 一个类即使什么都不写,也会存在一个方法
  3. 使用new关键字必须要有构造器,new本质是在调用构造器
  4. 构造器一般用来初始化值
  5. 有参构造;一旦定义了有参构造,就必须显示定义一个无参(无参里空着)
  6. 生成构造器快捷键 Alt+Insert
  7. 单例模式需要构造器私有(有疑问)
//这就是一个无参构造
public Person() {
}

//这是一个有参构造
 public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

一个测试实例

package com.oop.demo02;

//java----->class
public class Person {
    //一个类即使什么都不写,也会存在一个方法

    String name;
    int age;

    //实例化初始值,这是一个默认的无参构造器
    //1.使用new关键字必须要有构造器,new本质是在调用构造器
    //2.构造器一般用来初始化值
    public Person(){
        //this.name = "kong";
    }

    //有参构造;一旦定义了有参构造,就必须显示定义无参(无参里空着)
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

/*
//测试类
public class Application {
    public static void main(String[] args) {


        //使用new关键词实例化一个对象
        Person person = new Person("debao",3);
        System.out.println(person.name+" "+person.age+"岁");//debao


    }

 */

运行结果

debao 3岁

Process finished with exit code 0

4.创建对象内存分析

基础65集

  • 代码
package com.oop.demo03;

public class Pet {
    public  String name;
    public  int age;

    //如果不写,默认有无参构造
    //如果写了有参构造,就不会默认有无参构造
    public void shout(){
        System.out.println("叫了一声");

    }
}

/*
//测试类
public static void main(String[] args) {
        Pet dog = new Pet();//dog是引用变量名,指向堆中具体的对象
        dog.name = "旺财";
        dog.age = 3;

        Pet cat = new Pet();
        cat.name = "如花";
        cat.age = 2;


        dog.shout();
        System.out.println(dog.name);
        System.out.println(dog.age + "岁了");
        System.out.println("=========");
        System.out.println(cat.name);
        System.out.println(cat.age + "岁了");

    }
 */
  • 运行结果
叫了一声
旺财
3岁了
=========
如花
2岁了

Process finished with exit code 0
  • 内存分析
  1. 方法区也属于堆
  2. 首先加载Application类到方法区
  3. 执行main()方法,将main()压入栈底
  4. new一个Pet
  5. 加载Pet类到方法区
  6. 生成一个具体对象dog,dog是在栈里面的,只是一个引用变量名(真实的对象是在堆里面)
  7. 栈存放引用,堆存放具体的对象
  8. 真正的对象是在堆里面的,通过Pet模板去new了一个Pet
  9. 此时属性都是默认值
Pet dog = new Pet();//dog是引用变量名,指向堆中具体的对象

面向对象编程java封装实例 面向对象编程 java_子类

  1. 给name和age赋值
dog.name = "旺财";
dog.age = 3;

面向对象编程java封装实例 面向对象编程 java_子类_02

  1. 同理,再来一只小猫(方法区也属于堆)

面向对象编程java封装实例 面向对象编程 java_面向对象编程java封装实例_03

5.类与对象小结

类与对象

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

方法

定义,调用!

对象的引用

  • 引用类型:八大基本类型
  • 对象是通过引用来操作的:栈----->堆

属性:字段Field 成员变量

  • 默认初始化
  • 数字: 0 0.0
  • char:u0000
  • boolean:false
  • 引用:null
  • 修饰符 属性类型 属性名 = 属性值

对象的创建和使用

  • 必须使用关键词创建对象,构造器 Preson kong = new Preson();
  • 对象的属性 kong.name
  • 对象的方法 kong.sleep()

类:

  • 静态的属性 属性
  • 动态的行为 方法

后续学习

  • 封装
  • 继承
  • 多态

6.封装

概念

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

思想

  • 该露的露,该藏的藏
  • 追求”高内聚,低耦合“。高内聚:就是类的内部操作细节自己完成,不允许外部干涉,低耦合:仅暴露少量的方法给外部使用
  • 记住这句话:属性私有,get/set
  • 私有属性不能直接调用,需要用get/set方法
  • Alt+Insert可以自动生成get/set属性

封装的意义

  • 提高代码安全性,保护数据
  • 隐藏代码的实现细节
  • 统一接口
  • 系统可维护性增加

私有封装实例

package com.oop.demo04;

//private 私有
public class Student {

    //属性私有
    private String name;    //名字
    private int id;    // 学号,
    private char sex;    //性别
    private int age; //年龄

    //提供一些可以操作这个属性的方法!
    //提供一些public的get和set方法

    //get 获得这个私有属性
    public String getName(){
        return this.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 void setName(String name){
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        //可以判断合法性
        if(age > 120 || age < 0){
            this.age = 3;
        }
        else {this.age = age;
        }
    }
    // 学习(),睡觉()


}

/*
//测试类

package com.oop;

import com.oop.demo03.Pet;
import com.oop.demo04.Student;

public class Application {

    public static void main(String[] args) {

        Student s1 = new Student();
        s1.getName();//通过getName方法获取私有属性

        s1.setName("孔");//通过setName设置属性值
        System.out.println(s1.getName());


        //设置年龄
        s1.setAge(50);
        System.out.println(s1.getAge());

    }
}

 */

7.继承

基础知识

  • 继承本质是对某一批类的抽象,从而对现实世界更好的建模。
  • Java中类只有单继承,没有多继承(一个儿子只能有一个爸爸,但是一个爸爸可以有多个儿子)
  • 继承是类和类之间的关系,除此之外,类和类之间的关系还有依赖,组合,聚合等。
  • 继承关系的两个类,一个为子类(派生类),一个为父类(基类)。子类继承父类,使用关键字extends来表示。
  • 子类和父类之间,从意义上讲应具备有“is a”的关系
  • extends的意思是扩展,子类是父类的扩展,用法如下
package com.oop.demo05;

//Person 人 /父类/基类
public class Person {
}
package com.oop.demo05;

//Student is a Person /派生类/子类
//extends 继承
public class Student extends Person{
}

子类继承父类

  • 子类继承父类就会拥有父类的全部方法
  • 只可以继承公共的方法public,不可以继承private(一般继承方法都是public,属性是private)
  • 几种修饰符的优先级
  • public
  • protected 受保护的
  • default 默认权限
  • private
  • 例子:子类
package com.oop.demo05;

//Student is Person
//extends 继承
public class Student extends Person{
    public static void main(String[] args) {

        Student student = new Student();
        student.say();
        System.out.println(student.money);

    }
}
  • 例子:父类
package com.oop.demo05;

//Person 人
public class Person {

    public int money = 10_0000_0000;

    public void say(){
        System.out.println("说了一句话");
    }
}
  • 运行结果
说了一句话
1000000000

Process finished with exit code 0
  1. Object类
  • 快捷键Ctrl + H打开查看继承树
  • 在Java中,所有的类都默认直接或者间接继承Object
public class Person /*extends Object*/{
}//是否声明继承Object类都无所谓,会自动继承

super和this的注意点

  • super注意点
  • super调用父类的构造方法时,必须在构造方法的第一个
  • super必须只能出现在子类的方法或者构造方法中!
  • super和this不能同时调用构造方法
  • super和his异同
  • 代表的对象不同
    this:本身调用者这个对象
    super:代表父类对象的应用
  • 前提
    this:没有继承也可以使用
    super:只能在继承条件下使用
  • 构造方法
    this:本类的构造
    super:父类的构造

super和this的一般应用

//测试类
package com.oop;

import com.oop.demo03.Pet;
import com.oop.demo05.Student;

public class Application {

    public static void main(String[] args) {

        Student student = new Student();
        student.test("德宝");

    }
}
//子类
package com.oop.demo05;

//Student is Person
//extends 继承
public class Student extends Person{
    
    private String name = "debao";

    public void test(String name){
        System.out.println(name);      //形参
        System.out.println(this.name); //此类中的name,debao
        System.out.println(super.name);//父类中的name,kong

    }
    
}
//父类
package com.oop.demo05;

//Person 人
public class Person /*extends Object*/{

    protected String name = "kong";
}

测试结果

德宝
debao
kong

Process finished with exit code 0

Note:私有的方法或属性无法被继承

super和this构造方法的应用

- 只要写了有参构造后,无参构造就没了;
  - 父类只有有参,没有无参的话,子类也是不能写无参的;
  - 所以写有参的话,必须写一个无参,无参可以是空的;
//测试类
package com.oop;

import com.oop.demo03.Pet;
import com.oop.demo05.Student;

public class Application {

    public static void main(String[] args) {

        Student student = new Student();


    }
//子类
package com.oop.demo05;

//Student is Person
//extends 继承
public class Student extends Person{

    //这个地方默认调用了父类的无参构造:super();
    //这个super()如果要写出来,必须要写在子类构造器第一行
    //构造器中super()和this()不能同时调用
    public Student() {
        System.out.println("Student无参构造执行了");
    }
}
//父类
package com.oop.demo05;

//Person 人
public class Person /*extends Object*/{

    public Person() {
        System.out.println("Person无参构造执行了");
    }
}

运行结果

Person无参构造执行了
Student无参构造执行了

Process finished with exit code 0

方法的重写(区别于重载)

  • 重写与重载

重载是参数名相同,参数列表不同;重写是子父类才有,子类重写父类的方法

  • 重写:需要有继承关系,子类重写父类的方法
  1. 方法名必须相同
  2. 参数列表必须相同
  3. 修饰符:范围可以扩大,但不能缩小; public>protested>default>private
  4. 抛出的异常:范围可以被缩小,但不能扩大;Exception(大) -->ClassNotFoundException
  • 重写定义:子类的方法和父类必须要一致;方法体不同
  • 为什么要重写:
  1. 父类的功能,子类不一定需要,或者不一定满足
  2. 快捷键 Alt + Insert
  • 静态方法的重写
  • 静态方法是类的方法,而非静态方法是对象的方法,有static时,b调用了B类的方法,因为b是用类B定义的,没有static时,b调用的是对象的方法,而b是用A类new的,即b是A new出来的对象,因此调用A方法。
  • 非静态方法的重写
    子类重写了父类的方法,执行子类的方法

面向对象编程java封装实例 面向对象编程 java_父类_04


8.多态

  • 概念:同一个方法可以根据发送对象不同而采取多种不同的行为方式
  • 一个对象的实际类型是确定的,但可以指向对象的引用类型有很多
  • 多态注意事项
  1. 多态是方法的多态,属性没有多态;
  2. 必须是父类和子类,有联系,否则类型转换异常!ClassCastException!
  3. 存在条件:
  • 继承关系
  • 方法需要重写(比如父类子类都有run()方法,要执行哪一个呢?)
  • 父类的引用指向子类 Father f1 = new Son()
  1. 有些方法不能被重写
  • static是静态方法,属于类,不属于实例(对象)
  • final 常量
  • private 私有方法

面向对象编程java封装实例 面向对象编程 java_父类_05

9.instanceof和类型转换

instanceof的应用

主要判断x与y是否存在父子关系

System.out.println(x instanceof y);
//这个代码是不是能编译通过,主要取决x与y是否存在父子关系
//结果是ture还是false,主要看x所指向的实际类型是不是y的子类型
  • 例子
package com.oop;

import com.oop.demo07.*;

public class Application {

    public static void main(String[] args) {
        /*
        各类的关系说明
        Object是任何类的父类
        Person是父类
        Student是子类
        Teacher是子类
        */
        Object object = new Student();

        System.out.println(object instanceof Student);//ture
        System.out.println(object instanceof Person);//ture
        System.out.println(object instanceof Object);//ture
        System.out.println(object instanceof Teacher);//false
        System.out.println(object instanceof String);//false

        Person person = new Student();

        System.out.println("==============================");
        System.out.println(person instanceof Student);//ture
        System.out.println(person instanceof Person);//ture
        System.out.println(person instanceof Object);//ture
        System.out.println(person instanceof Teacher);//false
        //System.out.println(person instanceof String);//编译报错  person和String没有半毛钱联系,所以报错

        Student student = new Student();
        System.out.println("==============================");
        System.out.println(student instanceof Student);//ture
        System.out.println(student instanceof Person);//ture
        System.out.println(student instanceof Object);//ture
        //System.out.println(student instanceof Teacher);//编译报错  Student和Teacher同级,不能比较,所以报错
        //System.out.println(student instanceof String);//编译报错  person和String没有半毛钱联系,所以报错
    }
}
  • 运行结果
true
true
true
false
false
==============================
true
true
true
false
==============================
true
true
true

Process finished with exit code 0

类型转换

基本的类型转换
  • 类型优先级 低-----高
  • byte<short<char<int<long<float<double //小数优先级一定大于整数
  • 强制类型转换(高到低)
int i=128;
byte b=(byte)i;
//强制转换  (类型)变量名;
  • 自动类型转换(低到高)
int i=128;
double b=i;
  • 不能对boolean转换
  • 不能转换为不相关类型
  • 高—>低强制转换
  • 可能出现内存溢出或者精度损失
方法的类型转换
  • 总结
  1. 只能父类引用指向子类的对象
  2. 把子类转换为父类(向上转型),不用强制转换,可能丢失一些方法;
  3. 把父类转换为子类(向下转型),强制转换
  4. 方便方法的调用,减少重复的代码!简洁!
  • 子类(低)----->父类(高)
    子类转父类可能丢失自己本来的一些方法
package com.oop;

import com.oop.demo07.*;

public class Application {

    public static void main(String[] args) {
     /*
        各类的关系说明
        Object是任何类的父类
        Person是父类        有run()方法
        Student是子类       有go()方法
        Teacher是子类
     */

        Student a = new Student();
        a.go();
        Person b = a;
        
        b.go();  //报错!!! b已经是Person类,不能调用Student方法,
        //子类转父类可能丢失自己本来的一些方法
      
    }
}
  • 父类(高)---->子类(低)-----强制转换
package com.oop;

import com.oop.demo07.*;

public class Application {

    public static void main(String[] args) {
     /*
        各类的关系说明
        Object是任何类的父类
        Person是父类        有run()方法
        Student是子类       有go()方法
        Teacher是子类
     */

        Person a = new Student();
        //a.go();    //Person父类执行Student子类的独有方法,报错
        
        //将Person类型的a转化为Student类型,就可以用Student类型的方法了  高---->低
        //Student a1 = (Student) a;//强制转换 方法一
        ((Student)a).go();         //强制转换 方法二
    }
}

10.static关键字详解

注意:

  • 通过final修饰的类不能被继承,没有子类(断子绝孙)
  • static与类一起加载
  1. static静态属性
package com.oop.demo08;

public class Student {

    //静态属性
    private static int age;  //静态变量
    private double score;   //非静态变量

    public static void main(String[] args) {
        Student s1 = new Student();

        //静态变量推荐用类调用,非静态不行
        System.out.println(Student.age);  //跟类有关
        System.out.println(Student.score);//跟对象有关,用类调用会报错
        System.out.println(s1.age);
        System.out.println(s1.score);
        
    }
}
  1. static静态方法
  • 非静态方法里面可以直接调用静态方法
  • 静态方法里不能直接调用非静态方法
  • 因为static和类一起加载
package com.oop.demo08;

public class Student {

    //方法
    //非静态方法
    public  void run(){
        go();
    }

    //静态方法
    public static void go(){

    }

    public static void main(String[] args) {
       //run不可以直接调用
        new Student().run();

       //go可以直接调用
        go();

    }
}
  1. 代码块
package com.oop.demo08;

public class Person {

    {
        System.out.println("匿名代码块"); //2.每次执行对象时都要执行,用来赋初始值
    
    }

    static {
        System.out.println("静态代码块"); //1.只执行一次
    }

    public Person() {
        System.out.println("构造方法");  //3.对象创建,先走匿名代码块,再走构造方法
    }

    public static void main(String[] args) {
        Person person1 = new Person();
        System.out.println("=======================");
        Person person2 = new Person();
    }
}
  • 运行结果
静态代码块
匿名代码块
构造方法
=======================
匿名代码块
构造方法

Process finished with exit code 0
  1. 导入包
package com.oop.demo08;

import static java.lang.Math.PI;
public class Test {
    public static void main(String[] args) {
        System.out.println(Math.random());//返回随机数
        System.out.println(PI);
        //不想写Math的话,可以把Math包导进去
        //import java.lang.Math
        //也可以导入里面的方法
        //import static java.lang.Math.random;叫静态导入包
        //然后就可以直接调用
        //System.out.println(random())

    }
}
  • 运行结果
0.11463553175833108
3.141592653589793

Process finished with exit code 0

11.抽象类

注意:

  • abstract修饰符可以用来修饰方法,也可以用来修饰类;如果修饰方法,那么该方法就是抽象方法,如果修饰类,那么该类就是抽象类;
  • 抽象类中可以没有抽象方法,可以写普通方法,但抽象方法的类一定要声明为抽象类;
  • 抽象类,不能使用new关键字来创建对象,他是用来让子类继承的;
  • 抽象方法,只有方法的声明,没有方法的实现,它是用来让子类实现的;
  • 子类继承抽象类,必须要实现抽象类中没有实现的抽象方法,否则该子类也声明为抽象类;
  • 抽象类也是类,类只能单继承,但是接口可以多继承!

作业:

  • 既然不能new,那存在构造器吗:存在
  • 存在的意义:把功能抽象出来,提高开发效率和可扩展性
  1. 这是一个抽象类
package com.oop.demo09;

//abstract抽象类
public abstract class Action {

    //是一种约束~可以后续有人帮我们实现~
    //abstract~ 抽象方法~ 只有方法名字,没有方法的实现
    public abstract void doSomething();
}
  1. 子类继承抽象类,必须要实现抽象类中没有实现的抽象方法,除非该子类也声明为抽象类
package com.oop.demo09;

//子类继承抽象类,必须要实现抽象类中没有实现的抽象方法,除非该子类也声明为抽象类;
public abstract class A extends Action{
}

----------------------------------------------------------------------------------------

package com.oop.demo09;

public class B extends Action{

    //子类继承抽象类,必须要实现抽象类中没有实现的抽象方法,除非该子类也声明为抽象类;
    @Override
    public void doSomething() {

    }
}

12.接口

重点:锻炼抽象思维

总结:

  • 接口是约束
  • 定义一些方法,让不同的人实现
  • 接口中的所有定义其实都是抽象的 都默认加了 public abstract
  • 所以定义的属性默认为静态常量public static final
  • 接口不能直接被实例化~因为接口中没有构造方法
  • implements可以实现多个接口
  • 子类实现接口,必须要重写接口中的方法~
  • 只有一个方法的接口叫做函数式接口,可以使用lambda表达式简化
  1. 定义接口,用interface关键词
package com.oop.demo10;
//interface 定义接口的关键字,接口都需要有实现类
public interface UserService {
    //接口中的所有定义其实都是抽象的 都默认加了 public abstract
    //接口中不能定义具体的方法


    //接口中可以定义为变量,常量
    //所以定义的属性默认为静态常量public static final
    public static final int AGE = 99;

    void add(String name);
    void delete(String name);
    void updata(String name);
    void query(String name);

}
package com.oop.demo10;

public interface  TimeService {
    void timer();
}
  1. 实现接口功能的类:用implements关键词
    Java是单继承,只能继承一个父类,但是一个类可以实现多个接口
package com.oop.demo10;

//抽象类:只能用extents
//类,可以实现接口 通过implements关键词  接口
//实现了接口的类,就需要重写接口中的方法

//利用接口实现多继承~
public class UserServiceImpl implements UserService,TimeService{

    @Override//重写
    public void add(String name) {

    }

    @Override
    public void delete(String name) {

    }

    @Override
    public void updata(String name) {

    }

    @Override
    public void query(String name) {

    }

    @Override
    public void timer() {

    }
}

13.内部类

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

注意:

  • 内部类如果是static静态类且外部类是非静态,则不能调用外部类的属性,因为静态类很早就开始运行了
  • 外部类和内部类
package com.oop.demo11;

public class Outer {

    private  int id;
    public void out(){
        System.out.println("这是外部类的方法");
    }

   public class Inner{
        public void in(){
            System.out.println("这是内部类的方法");
        }

        //好处:可以获得外部类的私有属性~私有方法
       public void getID(){
           System.out.println(id);
       }
    }
}


/*
测试类
package com.oop;

import com.oop.demo11.Outer;

public class Application {

    public static void main(String[] args) {

        //new
        Outer outer = new Outer();

        //通过外部类来实例化内部类~
        Outer.Inner inner = outer.new Inner();
        inner.in();

    }
}
*/
  • 其他的写法
package com.oop.demo11;

public class Outer {

}

//一个java类中可以有多个class类,但只能有一个public类
//这也算一个内部类
class A{
    public static void main(String[] args) {
        
    }
}
  • 局部内部类
package com.oop.demo11;

public class Outer {
    
    //局部内部类:方法中的类
    public void method(){
        class Inner{
            public void in(){
                
            }

         }
    }

}
  • 匿名内部类(重点):没有名字去初始化类,不用将实例保存在变量中~
package com.oop.demo11;

public class Test {
    public static void main(String[] args) {
        //实例化一个对象
        Apple apple = new Apple();
        //没有名字去初始化类,不用将实例保存在变量中~
        new Apple().eat();

        //也可以用这个想法去new接口
        UserService userService = new UserService(){

            @Override
            public void hello() {

            }
        };

    }
}

class Apple{
    public void eat(){
        System.out.println("eat");
    }
}

interface UserService{
    void hello();
}