Java面向对象编程(OOP)

面向过程&面向对象

面向过程思想:

  • 步骤清晰简单,第一步该做什么,第二步该做什么...
  • 适合处理一些较为简单的问题
    线性思维

面向对象四思想:

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

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

什么是面向对象

面向对象编程:Object-Oriented Programming,OOP
面向对象编程的本质:以类的方式组织代码,以对象的组织(封装数据)

抽象

三大特性:

  • 封装

  • 继承

  • 多态

    从认识论的角度考虑是先有对象后有类,对象,是具体的事物。类,是抽象的,是对对象的抽象
    从代码运行的角度考虑,是先有类后有对象,类是对象的模板

回顾方法及加深

方法的定义

  • 修饰符
  • 返回值类型
  • break:跳出switch,结束循环 和return的区别
  • 方法名(注意规范,驼峰命名 得见名知意)
  • 参数列表 (参数名,参数类型)(可变参数:参数类型+...)
  • 异常抛出

方法的调用:递归

  • 静态方法
  • 非静态的方法
  • 形参和实参
  • 值传递和引用传递
  • this关键字

方法回顾
1.什么是方法

package OOP;

//Demo01就是一个类
public class Demo01 {
    //main方法
    public static void main(String[] args) {

    }

    /*
    修饰符 返回值类型 方法名(参数){
        方法体
        return 返回值;
    }
     */
    //return 结束方法,返回一个结果
    public static String print(){
        return "hello";
    }
    public void a(){
        return;
    }
    public int max(int a,int b){
        return a > b ? a : b; //三元运算符
    }
}

2.方法的调用
首先创建一个学生类

package OOP;

//学生类
public class Student {

    //静态方法
    public static void say(){
        System.out.println("学生说话了");
    }

    public void Say(){
        System.out.println("学生没有说话");
    }
}

然后去调用学生类

public class Demo02 {
    public static void main(String[] args) {

        //静态方法调用
        Student.say();

        //非静态方法调用
        //实例化这个类 new
        //对象类型  对象名  =  对象值;
        Student student = new Student();
        student.Say();
    }

    //同一个类中,两个方法可以互相调用,但是当一个为静态方法,有static,一个为非静态方法,则不行
    //若加了static,它和类一起加载的
    public void a(){
        b();
    }
    //类实例化之后才存在
    public void b(){
        a();
    }
}

3.实参和形参

public class Demo03 {
    public static void main(String[] args) {
        //实际参数和形式参数的类型要一一对应
        //当下面方法为非静态时这样输出
        Demo03 a = new Demo03();
        int b = a.add(1,2);
        System.out.println(b);

        //当方法为静态时这样调用
//        int a = Demo03.add(1,2);
//        System.out.println(a);
    }

    //注意该位置有无static
    public static int add(int a, int b){
        return a+b;
    }
}

值传递

package OOP;

//值传递
public class Demo04 {
    public static void main(String[] args) {
        int a = 1;
        System.out.println(a);  //输出1

        Demo04.change(a);
        System.out.println(a);  //输出1,输出的依旧是上面那个a,change方法返回值为空

    }
    //返回值为空,将a=1传给该方法,但是走完之后未将该方法的值再传递回去
    public static void change(int a){
        a = 10;
    }
}

引用传递

package OOP;

//引用传递:对象,本质还是值传递
//对象,内存
public class Demo05 {
    public static void main(String[] args) {
        Person i = new Person();
        System.out.println(i.name); //未对其定义,输出null

        Demo05.change(i);
        System.out.println(i.name); //未对其定义,输出蔡徐坤
    }
    public static void change(Person i){
        //i是一个对象:指向的是--->Person i = new Person();这是一个具体的对象,就可以改变它的属性
        i.name = "蔡徐坤";
    }
}

//定义了一个person类,有一个属性:name
class Person{
    String name;
}

类与对象的关系

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

  • 动物,手机,电脑
  • Person类,Pet类,Car类等,这些都是用来描述/定义某一事物应该具备的特点和行为

对象是抽象概念的具体实例

  • 张三这个人就是人的具体实例,他家的狗就是狗的具体实例
  • 能够体现出特点,展现出功能的是具体实例,而不是一个抽象的概念

创建与初始化对象

使用new关键字创建对象

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

类中的构造器也称构造方法,是在创建对象时必须要调用的,构造器有以下俩特点:

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

构造器必须掌握

先创建一个学生类

package OOP.demo02;

//学生类
public class Student {

    //属性:字段
    String name;  //null
    int age;      //0

    //方法
    public void study(){
        System.out.println(this.name+"在学习");
    }
}

再创建一个运行方法

package OOP.demo02;

//一个项目应该只存在一个main方法
public class Application {
    public static void main(String[] args) {

        //类:抽象的,实例化
        //类实例化后会返回一个自己的对象
        //该地方student对象就是一个Student类的具体实例
        Student xiaoming = new Student();
        Student dongfeng = new Student();

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

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

        dongfeng.name = "东风";
        dongfeng.age = 18;
        System.out.println(dongfeng.name);
        System.out.println(dongfeng.age);
    }
}

ps:在类中,应只有属性和方法,启动的main方法应单独在一个类中

作用域

基本使用:

  • 1.在java编程中,主要的变量就是属性(成员变量)和局部变量
  • 2.我们说的局部变量一般是指在成员方法中去定义的变量
  • 3.java中作用域的分类:
    • 全局变量:也就是属性,作用域为整体类体,例如:在CAT类,cry,eat等方法使用属性
    • 局部变量:也就是除了属性之外的其他变量,作用域为定义在他的代码块中
  • 全局变量(属性)可以不赋值,直接使用,因为有默认值,局部变量必须赋值后,才能使用,因为没有默认值

话不多说,我们直接上代码

package javaSEStudy.basics.OOP.varScope;

public class VarScope1 {
    public static void main(String[] args) {

    }
}

class Cat{
    //全局变量,也就是属性,作用域为整个类体
    //属性在定义时可以直接赋值
    //在这里sex的作用域就在Cat这个类中
    char sex = '雄';

    //全局变量可以不赋值,因为有默认值,局部变量必须赋值才能使用,因为没有默认值
    //该默认值为0.0
    double weight;

    public void speak(){
        //需要将其赋值,也就是初始化
        int num;
        //这样直接使用报错,需要给num赋值才能直接使用
//        System.out.println(num);
    }

    public void cry(){
        //这里的name,age就是局部变量
        //name和age的作用域在cry中,出了这个方法哪儿都用不了
        String name = "jack";
        int age = 18;
        System.out.println("在cry中使用sex属性"+ sex);
    }

    public void eat(){
        System.out.println("在eat中使用sex属性" + sex);
    }
}

作用域注意事项和细节使用

  • 1.属性和局部变量可以重名,访问时遵循就近原则
  • 2.在同一个作用域中,比如在同一个方法成员中,两个局部变量不能重名
  • 3.属性生命周期较长,伴随对象的创建而创建,伴随对象的死亡而死亡,局部变量,生命周期较短,伴随着他的代码块的执行而创建,伴随着代码块的结束而死亡,即在同一个方法调用过程中
  • 4.作用域范围不同
    • 全局变量:可以在本类中使用,或其他类使用(通过方法调用)
    • 局部变量:只能在本类中对应的方法使用
  • 修饰符不同
    • 全局变量/属性可以添加修饰符
    • 局部变量不可以添加修饰符

让我们来举个栗子

package javaSEStudy.basics.OOP.varScope;

public class VarScopeDetail {
    public static void main(String[] args) {
        new Person().speak();
        //当执行speak方法时,speak的方法的局部变量比如name就会创建,当speak方法执行完毕后,就将name这个局部变量销毁
        //但是属性也就是全局变量仍然可以使用

        //第一种跨类
        new T().test1();
        //第二种跨类
        new T().test2(new Person());
    }
}

class T{
    public void test1(){
        //在别的类中调用属性
        System.out.println(new Person().name);
    }
    //把别的作用域的Person传递给它
    public void test2(Person p){
        System.out.println(p.name);
    }
}

class Person {
    //属性可以加修饰符,private public protected
    String name = "jack";

    public void speak(){
        //局部变量和全局变量也就是属性可以重名,访问时遵循就近原则
        //局部变量不能加修饰符
        String name = "king";
        System.out.println("speak中的name:" + name);
    }
}
  1. 核心机制:对象引用传递
    参数传递本质:Java中方法参数传递均为值传递,但对于对象类型(如Person),传递的是对象的引用地址副本(即指针的拷贝)。
    test2(Person p) 的运作:
    当调用 test2(new Person()) 时,实际是将新创建的 Person 对象的引用地址复制给形参 p,此时 p 和外部传入的 Person 实例指向堆内存中的同一对象。
Heap: [Person对象(name="jack")] ← 被 p 和外部匿名对象同时引用 

2.与 test1 的对比

对比维度 test1() test2(Person p)
对象创建方式 方法内新建 Person 对象 接收外部传入的 Person 对象
内存开销 每次调用新建对象,可能增加GC压力 复用现有对象,更高效
使用场景 无需外部数据时 需要跨方法/类协作时

3.潜在问题与注意事项

  • 空指针风险:需校验传入参数是否为 null(如 if (p != null))。
  • 副作用(Side Effect):
    若在 test2 内修改了 p.name,外部对象的属性也会同步改变(是否需防御性拷贝取决于设计需求)。
  • 线程安全:多线程环境下需考虑引用对象的线程安全性。

构造器详解

package OOP.demo02;

//java----->生成class文件
public class Person {
    //一个类即使什么都不写,也会存在一个方法,默认构造器
    //显示的定义构造器

    String name;


    //无参构造
    //1.使用new关键字必须要有构造器,本质是在调用构造器
    //2.实例化初始值
    public Person(){
        this.name = "蔡徐坤";  //一旦写了下面的有参构造,这个构造器必须写出来且为空
    }
    //有参构造
    //一旦定义了有参构造,无参就必须显示定义
    public Person(String name){
        this.name = name;
    }
}
/*
public static void main(String[] args) {
        //new 实例化一个对象
        Person person = new Person("蔡徐坤");  //倘若为无参构造可空,有参得给值
        System.out.println(person.name);
    }
    构造器:
    1.和类名相同
    2.无返回值
    作用:
    1.new 本质在调用构造器
    2.初始化对象的值
    注意点:
    1.定义有参构造后,如果想使用无参构造,显示的定义了一个无参构造,也就是空

    alt + insert 快捷插入构造器
    this.  =   ;this. 代表当前的值;= 后是传进来的值
 */

简单小结与对象

/*
        1.类与对象
            类是一个模板,抽象的;对象则是一个具体的实例
        2.方法
            定义、调用
        3.对象的引用
            引用类型:   基本类型(8个)
            对象是通过引用来操作的:栈 ----> 堆
        4.属性:字段filed 成员变量
            默认初始化 数字则是0,char则是:u0000 ,boolean:false ,引用:null

            修饰符 属性类型 属性名 = 属性值
        5.对象的创建和使用
            必须使用new关键字去创建对象,构造器 Person person = new Person
            对象的属性 caiXuKun.name
            对象的方法 caiXuKun.sleep();
        6.类:
            静态的属性   属性
            动态的行为   方法
        7.三大特征:
            封装
            继承
            多态
*/

封装

该露的露,该藏得藏
程序设计追求:高内聚,低耦合
高内聚:类内部的数据操作细节自己完成,不允许外部干涉
低耦合:仅暴露少量方法给外部使用

封装:数据的隐藏

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

记住一句话:属性私有,get/set
有时set里面会加一个安全性验证

举个栗子
先创建一个学生类

package OOP.demo04;

//学生类 private:私有
public class Student {

    //下面将public换成private就是将属性私有

    //名字
    private String name;
//    public String name;
    //学号
    private int id;
    //年级 grade:成绩,等级,年级
    private int grade;
    //性别
    private char sex;
    //年龄
    private int age;

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

    //get 获得这个数据
    public String getName() {
        return this.name;
    }
    //set 给这个属性设置值
    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        if (age >= 150 || age <= 0) {
            this.age = 0;
        }else {
            this.age = age;
        }

    }
    
    //这是方法
    //学习()

    //睡觉()

    /*
    alt + insert 也可快捷设置get和set方法
     */
}

然后创建一个测试类

package OOP.demo04;

public class Application {
    public static void main(String[] args) {
        Student s1 = new Student();
//        s1.name = "蔡徐坤";   将private换成pubilc才能这样读取
        //被封装后,以下面方式开始读取
        String name = s1.getName();
        //赋值
        s1.setName("牢大");
        System.out.println(s1.getName());

        s1.setAge(999); //该输入不合法
        System.out.println(s1.getAge());
    }
}

封装的意义:

  • 提高程序的安全性,保护数据
  • 隐藏代码的实现细节
  • 统一接口,形成规范
  • 提高系统的可维护性

继承

本质:是对某一批类的抽象,从而实现对现实世界更好的建模

extends的意思是“扩展”,子类是父类的扩展

java中只有单继承,没有多继承
一个儿子只有一个父亲,但是一个父亲可以有多个儿子

  • 继承是类与类的一种关系,除此以外,类与类之间的关系还有依赖,组合,聚合
  • 继承关系的两个类,一个为子类(派生类),一个为父类(基类)。子类继承父类,用关键字extends表示
  • 子类与父类之间从意义上讲,应该具有‘is a’的关系

object类
super
方法重写

我们在企业级应用开发中很少使用继承等,更多的是使用组合

方式 代码示例 适用场景 优点
构造方法注入 this.calculator1 = calculator1 强依赖 明确、线程安全
Setter 注入 setCalculator1(calculator1) 可选依赖 灵活
方法参数传递 handleCalculation(calculator1) 临时依赖 无长期耦合
接口依赖 private Calculator calculator 扩展性强 松耦合
依赖注入框架 @Autowired private Calculator1 calculator1 企业级应用 自动化管理

选择哪种方式?

  • 如果依赖是必须的、不变的 → 构造方法注入(推荐 final 修饰)。
  • 如果依赖是可选的、可变的 → Setter 注入。
  • 如果依赖只临时使用 → 方法参数传递。
  • 如果需要扩展性 → 接口依赖。
  • 如果是大型项目 → 依赖注入框架(如 Spring)。

让我们举个栗子
先定义一个父类

package OOP.demo05;

//在java中所有的类都默认直接或间接继承Object类  
//Person 人;父类、基类
public class Person {
    //public 公共的,子类可以调用
    //protected 受保护的,优先级比default高一点
    //private 私有的,子类无法调用
    //default 默认的

    private int money = 10_0000_0000;
    //删去private为默认,可以加该关键词,也可以不加,需要其他关键词换掉private即可

    public int getMoney() {
        return money;
    }

    public void setMoney(int money) {
        this.money = money;
    }
}

再给出他的两个子类

package OOP.demo05;

//Student is 人;派生类,子类
//子类继承了父类,就会拥有父类的全部方法
public class Student extends Person {
    //crtl + h 指出哪个类继承哪个类,给出关系示例图
}
package OOP.demo05;

//老师 is 人;派生类,子类
public class Teacher extends Person {
}

最后给出测试类

package OOP.demo05;

public class Application {
    public static void main(String[] args) {
        Student s = new Student();
        System.out.println(s.getMoney());
    }
}

可以看出,student类中并没有任何属性,但是研究可以调用money这个属性,他继承了person这个类的所有属性,所以可以调用

super详解

直接上代码展示
先写父类

package OOP.demo06;

public class Person {

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

    protected String name = "蔡徐坤";

    //假若这里修饰词是priva,即使用super也无法调用
    public void print(){
        System.out.println("鸡你实在太美");
    }
}

再写子类

package OOP.demo06;

public class Student extends Person {

    public Student() {
        //隐藏代码:调用了父类的无参构造 ----> super();
        super();    //调用父类的构造器,必须在子类构造器的第一行
        System.out.println("student执行无参构造");
    }
        //在这里有个问题,假如父类没有无参构造,那么子类也无法进行无参构造

    private String name = "基尼太美";
    public void test1(String name) {
        System.out.println(name);   //这是我传给他的参数
        System.out.println(this.name);  //是类里面的参数
        System.out.println(super.name); //用来访问父类里的参数
    }

    public void print(){
        System.out.println("good good study");
    }
    public void test2(){
        print();        //当前类的方法
        this.print();   //当前类的方法
        super.print();  //父类里面的方法
    }
}

再写测试类

package OOP.demo06;

public class Application {
    public static void main(String[] args) {

        Student student = new Student();
        System.out.println("==============");
        /*
        会输出:
        Person执行无参构造
        student执行无参构造
        这里未对父类进行实例化,但是依旧输出了父类
        证明先对父类进行无参构造,再对子类进行无参构造
         */

        student.test1("kunkun");
        System.out.println("===============");
        student.test2 ();
    }
}

/*
最终输出结果一览

Person执行无参构造
student执行无参构造
==============
kunkun
基尼太美
蔡徐坤
===============
good good study
good good study
鸡你实在太美

*/

super注意点:

  • super调用父类的构造方法,必须在构造方法的第一个
  • super必须只能出现在子类的方法或者构造方法中
  • super和this不能同时调用构造方法

VS this

  • 代表的对象不同: this ---> 本身调用者这个对象; super ----> 代表父类对象的应用
  • 前提:this ---> 没有继承也可以使用;super ----> 只能在继承条件下才能使用
  • 构造方法:this(); 默认调用本类的构造 super(); 父类的构造

方法重写

让我们举个栗子
先写一个父类,A类

package OOP.demo07;

//重写都是方法的重写,与属性无关
public class A {
    public void test(){
        System.out.println("A ----> test");
    }
}

再写一个子类,B类

package OOP.demo07;

//继承
public class B extends A {

    //override 重写
    @Override   //注解,有功能的注释
    public void test() {
//        super.test();   //默认调用父类的方法,也可重写自己的方法
        System.out.println("B ----> test");
    }
}

最后写一个测试类

ackage OOP.demo07;

public class Application {
    public static void main(String[] args) {
        //静态方法和非静态方法区别很大

        //在静态方法中:方法的调用只和左边定义的数据类型有关
        //非静态 -----> 重写
        //在重写的方法中,修饰词也只能为public,如果为private,则无法重写
        //重写的方法方法名和参数都一样,但是只能子类重写父类的方法
        B b = new B();
        b.test();   //走b类调用方法

        //父类的引用指向了子类
        A a = new B();
        //在非静态方法中,这叫子类重写了父类的方法
        a.test();   //走a类调用方法
    }
    /*
    在A,B类中,方法中加了static静态,输出为
    B ----> test
    A ----> test

    将static删除,输出
    B ----> test
    B ----> test
     */
}

重写:

  • 需要有继承关系,子类重写父类方法
  • 1.方法名必须相同
  • 2.参数列表必须相同
  • 3.修饰符:范围可以扩大,但是不能缩小: public > protected > default > private
  • 4.抛出的异常:范围可以被缩小,但是不能扩大

重写:子类的方法必须和父类方法一致,方法体不同
为什么需要重写?
1.父类的功能,子类不一定需要或者不被满足
快捷键:alt + insert ,选中 overide,也就是重写
2.当重写时,idea在代码行数边会出现小图标

多态

可以实现动态编译,增强可扩展性
也就是动态绑定机制:
当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定
当调用对象属性时,没有动态绑定机制,哪里声明,就在哪里使用
举个栗子:

B extends A

A a = new B();

//A 是a的编译类型,B是a的运行类型
//我们先把B类的sum方法注销
//当运行到父类也就是A类的时候,A类和B类有一个重名方法getI(),
//它需要去调用这个重名方法,那么它一定是走的B类的方法,所以这里B的return i 也就是20再加10,最后输出30

//再把A类的sum1注销掉,这里返回的是i,由于属性没有动态绑定机制,所以会返回20,父类的i=10 再加上 10,输出20

什么是多态:

  • 即同一方法可以根据发送对象的不同而采取多种不同行为方式
  • 一个对象的实际类型是确定的,但是可以指向对象的引用的类型有很多(父类或有关系的类)

多态存在的条件

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

注意
多态是方法的多态,属性没有多态性
instanceof
类型转换 --> 引用类型转换

让我们来举个栗子
先创建一个Person类

public class Person {
    public void run(){
        System.out.println("Student run");
    }
}

再创建一个学生类

public class Student extends Person {
    @Override
    public void run() {
        System.out.println("maike run");
    }

    public void eat(){
        System.out.println("eat");
    }
}
/*
    多态注意事项:
        1.多态是方法的多态,属性没有多态
        2.父类和子类,有联系,不行会报:类型转换异常 ClassCastException!
        3.存在条件: 继承关系,方法需要重写,父类引用指向子类对象 Father f1 = new Son();
            有些方法不能被重写:static 方法,属于类,他不属于实例
                            final 常量
                            private  该修饰的方法被私有,无法重写
 */

最后的惯例测试类

public class Application {

    public static void main(String[] args) {
        //一个对象的实际类型是确定的
//        new Student();
//        new Person();

        //可以指向的引用类型不确定:父类的引用指向子类
        //Student 能调用的方法都是自己的或者继承父类的!
        Student s1 = new Student();
        //Person 父类的,可以指向子类,但不能调用子类独有的方法
        Person s2 = new Student();
        Object s3 = new Student();

        //对象能执行哪些方法,主要看对象左边的类型和右边关系不大
        s2.run();   //子类重写了父类的方法,执行子类的方法
//        s2.eat();   //父类无方法,所以在这里他就不能这样去调用子类中的eat方法
        s1.run();
        ((Student) s2).eat();  //这样写才能从父类调用子类独有的方法
        //该写法是强制类型转换,将s2这个高类型转换为了Student这个低类型
    }
}

instanceof 和 类型转换

instanceof详解
让我们来举个栗子
首先创建一个person类

public class Person {
    public void run(){
        System.out.println("run");
    }
}

再创建一个student类

public class Student extends Person {
    public void go(){
        System.out.println("go");
    }
}

再创建一个teacher类

public class Teacher extends Person {
}

最后来个测试类

public class Application {
    public static void main(String[] args) {

        //Object > String
        //Object > Person > Teacher
        //Object > Person > Student
        // x instanceof y  能不能编译通过  接口

        Object s1 = new Student();
        System.out.println(s1 instanceof Student);      //true
        System.out.println(s1 instanceof Person);       //true
        System.out.println(s1 instanceof Object);       //true
        System.out.println(s1 instanceof Teacher);      //false
        System.out.println(s1 instanceof String);       //false

        System.out.println("============");

        Person s2 = new Student();
        System.out.println(s2 instanceof Student);      //true
        System.out.println(s2 instanceof Person);       //true
        System.out.println(s2 instanceof Object);       //true
        System.out.println(s2 instanceof Teacher);      //false
//        System.out.println(s2 instanceof String);  //编译就报错,person与string没有任何联系

        System.out.println("============");

        Student s3 = new Student();
        System.out.println(s3 instanceof Student);      //true
        System.out.println(s3 instanceof Person);       //true
        System.out.println(s3 instanceof Object);       //true
//        System.out.println(s3 instanceof Teacher);      //编译报错,teacher与student无任何关系
//        System.out.println(s2 instanceof String);       //编译就报错,person与string没有任何联系
    }
}

类型转化
让我们举个栗子
除了测试类与instanceof中不一样,其余都一样,在这里就不重复了

public class Application {
    public static void main(String[] args) {
        //类型之间的转换  基本类型转换  高低(64 32 16 8)
        //             父 ------> 子
        //             高 ------> 低
        //当子类转换为父类,可能会丢失一些自己本来的方法
        Person s1 = new Student();
//        s1.go;     这样无法直接调用,这是子类独有的方法,父类无法调用
        //s1将Person类型,也就是这个对象,转换为Student类型,就可以调用Student类型的方法
        ((Student) s1).go();

        //这两个代码本质相同,下面这个代码是对上面代码最直观的解释
        Student s2 = (Student) s1;
        s2.go();
    }
}
/*
1.父类引用指向子类的对象
2.把子类转换为父类,向上转型:可以直接转换
3.把父类转换为子类,向下转型:需要进行强制类型转换(再基本类型中会丢失精度,在这里可能会丢失一些方法)
4.方便方法的调用,减少重复代码

抽象 :  封装  继承   多态

 */

static关键字详解

static 变量是类中所有对象共享
static 类变量,在类加载的时候就生成
它存在堆中的GC堆中,和Class对象一样
让我们举个栗子

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 p1 = new Person();
        System.out.println("=============");
        Person p2 = new Person();
    }
    /*
    静态代码块
    匿名代码块
    构造方法
     */
}
package OOP.demo10;

//static
public class Student {
    private static int age;  //静态的变量    多线程
    private double score;    //非静态变量

    public static void go(){

    }
    public void run(){
        go();  //非静态方法也可以这样直接调用静态方法
    }

    public static void main(String[] args) {
        new Student().run();  //通过new一个对象才能用一点去调用这个方法
        Student.go();  //go方法在这个类中,他是静态方法,可以直接使用类名去调用这个方法
        go();          //也可以猴这样直接调用该方法

//        Student s1 = new Student();
//
//        System.out.println(Student.age);
////        System.out.println(Student.score);    无法使用类名加一点去调用
//
//        System.out.println(s1.age);
//        System.out.println(s1.score);
    }


    {
        //代码块(匿名代码块)
    }

    static {
        //静态代码块
    }

}
package OOP.demo10;

import java.sql.SQLOutput;

import static java.lang.Math.random;  //静态导入包
import static java.lang.Math.PI;  //静态导入包

public class Test {
    public static void main(String[] args) {
        System.out.println(random());  //输出一个随机数,math类中的
        System.out.println(Math.random());  //如果没有导包,得这么写
        System.out.println(PI);
    }
}

理解main方法语法

public static void main(String[] args)
java虚拟机需要调用类的main()方法,所以该方法的访问权限必须是public
java虚拟机在执行main()方法的时候不必创建对象,所以方法必须是static
该方法接受String类型的数组参数,该数组保存执行java命令时所传递给所运行的类的参数

在运行的时候将参数传入给虚拟机使用也就是 java执行程序 参数1 参数2 参数3

抽象类

abstract修饰符可以用来修饰方法也可以用来修饰类,如果修饰方法,那么该方法就是抽象方法,如果修饰类,那么该类就是抽象类
抽象类中可以没有抽象方法,但是有抽象方法的的类一定要声明为抽象类

在抽象类中,不能用new关键字来创建对象,他是用来让子类继承的
抽象方法,只有方法的声明,没有方法的实现,他是用来让子类实现的

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

我们直接来举个栗子

package OOP.demo11;

//abstract  将该类变成抽象类
//extends java里是单继承,没有多继承,类只能单继承,但是接口可以多继承
public abstract class Action {

    //约束 有人帮我们实现
    //抽象方法,该方法没有实现的功能模块,只有一个方法名
    public abstract void doSomething();
}

再创建一个继承他的类

public class A extends Action {

    /*因为A继承Action,但是A为抽象类,其中有抽象方法
      我们在创建该类去继承的时候就会报错,要求我们必须重写其中被抽象的方法
      抽象类的所有方法,继承了它的子类,都必须实现它的方法
     */
    //除非它的子类也是abstrict抽象类,那就不用实现
    @Override
    public void doSomething() {

    }
}
/*
    特点:
    1.不能new这个抽象类,只能通过子类去实现,他就是一个约束
    2.抽象类里可以有普通方法,但是抽象方法必须在抽象类中

    抽象的抽象:约束
    存在意义:一个角色的创建,可以是继承他的方法,然后将公有属性抽象出来,就可以是代码更简洁高效
 */

接口的定义与实现

普通类:只有具体实现
抽象类:具体实现和规范(抽象方法)都有
接口:只有规范,自己无法写方法,专业约束,约束和实现分离:面向接口编程

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

接口就是规范,定义的是一组规则,体现了现实生活中“如果你是...则必须能...”的思想,如果你是汽车,则必须能跑

接口的本质就是契约,就像现实中的法律,制定好后大家遵守

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

我们先创建两个接口

package OOP.demo12;

//抽象的思维
//interface 接口的关键字,每个接口都需要有他的实现类
public interface UsersService {

    //接口中所有定义的属性都是一个静态常量
    //前面使用的关键字是public static final
    int age = 64;

    //接口中的所有定义方法都是抽象的,其默认为public abstract
    void add(String name);
    void delete(String name);
    void update(String name);
    void query(String name);
}
package OOP.demo12;

public interface TimeService {
    void timeer();
}

我们再创建一个接口的实现类

package OOP.demo12;

//类可以实现接口,且命名为接口后加Impl,记得在类中写入implement去连接接口
//实现接口的类,必须要重写接口里所有的方法
//从侧面实现了多继承 利用接口
public class UserServiceImpl implements UsersService,TimeService {  //该写法实现了多继承,他同时继承了UsersService,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 timeer() {

    }
}

接口作用:
1.约束
2.定义一些方法,让不同的人实现
3.默认方法前的修饰词都是public abstract,将其抽象了
4.我们定义一个常量,他前面的修饰词为public static final
5.接口不能被实例化,接口中没有构造方法
6.implement可以实现多个接口
7.实现接口必须要重写里面的方法

内部类

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

  • 成员内部类
  • 静态内部类
  • 局部内部类
  • 匿名内部类

我们直接来举个栗子
先创建一个外部类

public class Outer {

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

    //class也可以写在方法中,这为局部内部类
    public void method(){
        class Inner{

        }
    }

    //当在这里加上static是,这个就变成静态内部类,下面就无法获取到id了
    public class Inner {
        public void in(){
            System.out.println("这是一个内部类的方法");
        }
        //获得外部类的私有属性,私有方法等
        public void getId(){
            System.out.println(id);
        }
    }

}
//一个java类中可以有多个class类,但是只能有一个public class
class A{

}

我们再建一个例子类

public class Test {
    public static void main(String[] args) {

        //匿名内部类
        //没有名字初始化类,不用将实例保存到变量中
        new Apple().eat();

        UserService userService = new UserService() {
            @Override
            public void hello() {

            }
        };
    }
}

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

interface UserService{
    void hello();
}
posted @ 2025-03-15 16:04  sprint077  阅读(26)  评论(0)    收藏  举报