美文网首页
Java面向对象编程

Java面向对象编程

作者: Java编程日记 | 来源:发表于2022-03-23 14:54 被阅读0次

    Java面向对象

    初识面向对象

    面向过程 & 面向对象

    面向过程思想

    • 步骤清晰简单,第一步做什么,第二部做什么...
    • 面对过程适合处理一些较为简单的问题

    面向对象思想

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

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

    什么是面向对象

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

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

    抽象:白话理解就是抽像,把像的的抽取出来,具体的理解还是抽像

    三大特征:

    • 封装
    • 继承
    • 多态

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

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

    这些看不懂没关系,后面慢慢的就会恍然大悟!

    回顾方法及加深

    一个真正的程序里面只有一个main方法,并不会像我们测试的时候每个类里面都有,我们只是为了方便代码的运行

    回顾方法的定义

    • 修饰符

    • public:公共的,所有人都可以调用这个方法

    • static:静态,方便调用 (可选)

    • 返回类型

    • void:空的:不需要使用return返回一个值(但是可以使用return结束方法)

    • 其他类型:需要使用return返回一个值,返回的值的类型要与其相对应

    • break和return的区别

    • break:跳出switch语句,和结束整个循环;还有一个continue是结束一次循环

    • return:结束当前方法,返回一个结果(可以为空)

    • 方法名

    • 注意规范即可:见名知意,首字母小写+驼峰原理

    • 参数列表

    • (参数类型 参数名,...) 可以多个,还有一个 可变参数(参数类型... 参数名)

    package com.xiaodi.operator.oop.demo01;
    
    //Demo01 类
    public class Demo01 {
    
        //main 方法
        public static void main(String[] args) {
    
        }
    
        /*
        方法的定义:
            修饰符 返回值类型 方法名(...) {
                //方法体
                return 返回值;
            }
        */
        public String sayHello() {
            return "Hello,World";
        }
    
        public double max(double a, double b) {
            return a > b ? a : b; //三元运算符
        }
    }
    
    • 异常抛出疑问 (后面讲解)

    回顾方法的调用

    • 静态方法

    • 静态方法:加static的为静态方法

    • package com.xiaodi.operator.oop.demo01; //学生类 public class Sudent { //静态方法 static public static void say() { System.out.println("学生说话"); } }

    • 静态方法的调用

    • package com.xiaodi.operator.oop.demo01; public class Demo02 { public static void main(String[] args) { //静态方法调用:类名.方法名; Sudent.say(); } }

    • 非静态方法

    • 非静态方法:没加static的为非静态方法

    • package com.xiaodi.operator.oop.demo01; //学生类 public class Sudent { //非静态方法 public void say() { System.out.println("学生说话"); } }

    • 非静态方法的调用

    • package com.xiaodi.operator.oop.demo01; public class Demo02 { public static void main(String[] args) { //非静态方法调用 //先实例化这个类 new ;对象类型 对象名 = new 对象值; Sudent sudent = new Sudent(); //然后调用 sudent.say(); } }

    • 特殊情况 原因:static 是和类一起加载的,这个类存在的时候它就存在,时间片非常早;普通方法是类实例化后才存在的

    • 两个普通方法(非静态) 或 两个静态方法:可以直接相互调用,无需实例化

    • package com.xiaodi.operator.oop.demo01; public class Demo02 { public static void main(String[] args) {} //两个普通方法(非静态):可以直接相互调用,无需实例化 public void a() { b(); } public void b() {} public static void c() { d(); } public static void d() {} }

    • 两个方法中一个一个为普通方法,一个为静态方法,如果静态方法调用非静态,就要实例化;非静态调用静态无须实例化

    • package com.xiaodi.operator.oop.demo01; public class Demo02 { public static void main(String[] args) {} public static void a() { Demo02 demo02 = new Demo02(); demo02.b(); } public void b() {} }

    • 形参和实参:形式参数和实际参数的类型要对应

    • 形式参数:是定义方法时的参数;例如:如下代码的(int a, int b)就是形式参数

    • package com.xiaodi.operator.oop.demo01; public class Demo03 { public static void main(String[] args) { } public int add(int a, int b) { return a+b; } }

    • 实际参数:是调用方法传递给方法的参数;例如:如下代码的demo03.add(1, 2);

    • package com.xiaodi.operator.oop.demo01; public class Demo03 { public static void main(String[] args) { Demo03 demo03 = new Demo03(); demo03.add(1,2); } public int add(int a, int b) { return a+b; } }

    • 值传递和引用传递

    • 值传递引用传递的概念

    • 值传递是指在调用方法时将实际参数复制一份传递到方法中,这样在方法中如果对参数进行修改,将不会影响到实际参数。

    • package com.xiaodi.operator.oop.demo01; //值传递 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 } //返回值为空 public static void change(int a) { a = 10; } }

    • 所谓引用传递是指在调用方法时将实际参数的地址传递到方法中,那么在方法中对参数所进行的修改,将影响到实际参数。

    • package com.xiaodi.operator.oop.demo01; //引用传递:java本质还是值传递 public class Demo05 { public static void main(String[] args) { Perosn perosn = new Perosn(); System.out.println(perosn.name);//nell Demo05.change(perosn); System.out.println(perosn.name);//晓迪 } public static void change(Perosn perosn) { perosn.name = "晓迪"; } } //定义了一个Perosn类,有一个属性:name class Perosn { String name;//null }

    • 值传递和引用传递的理解值传递 在方法的调用过程中,实参把它的实际值传递给形参,此传递过程就是将实参的值复制一份传递到方法中,这样如果在方法中对该值(形参的值)进行了操作将不会影响实参的值。因为是直接复制,所以这种方式在传递大量数据时,运行效率会特别低下。引用传递 引用传递弥补了值传递的不足,如果传递的数据量很大,直接复过去的话,会占用大量的内存空间,而引用传递就是将对象的地址值传递过去,方法接收的是原始值的首地址值。在方法的执行过程中,形参和实参的内容相同,指向同一块内存地址,也就是说操作的其实都是源数据,所以方法的执行将会影响到实际对象。

    • java本质还是值传递(为什么这么说: 引用传递是里面存的首地址的copy。原来引用变量里的的对象你不能改,但是他指向的堆对象你可以改啊。 )

    • 结论:值传递,相当于拷贝一份值,对参数的修改不影响原有参数引用类型传引用,形参和实参指向同一个内存地址(同一个对象),所以对参数的修改会影响到实际的对象。

    上面还写了一个class(剧透了内部类的内容(后面会讲)) 一个类里面只能有一个public class 但是能有多个class

    • this关键字 (讲到继承的时候再讲)

    值传递和引用传递大家肯定觉得很绕,这是因为我们对 对象和从内存分析的理解还不透彻,本章后面后面的内容学完再来看一遍,肯定会恍然大悟!

    对象的创建*

    类与对象的关系

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

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

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

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

    创建与初始化对象

    以后我们就不要在每一个类里面都去加上main方法,一个程序只有一个主启动类

    使用new关键字创建对象

    package com.xiaodi.operator.oop.demo01.demo02;
    
    //学生类
    public class Student {
        //不管再厉害的人写的类里面只可能存在两个东西,1就是属性 2就是方法
        //属性:可以理解为字段
        String name;
        int age;
    
        //方法
        public void study() {
            //this.代表当前这个类的
            System.out.println(this.name+"在学习");
        }
    }
    

    这样我们一个简单的类就定义出来了

    package com.xiaodi.operator.oop.demo01.demo02;
    
    //一个项目应该只存在一个main方法
    public class Application {
        public static void main(String[] args) {
            //类是抽象的,我们需要把这个类实例化
            //类实例化后会返回一个自己的对象!
            //student对象就是一个Student类的具体实例!
            Student student = new Student();
    
            Student xiaoming = new Student();
            Student xiaohong = new Student();
            //类是抽象的可以这么理解:类就相当一个模板,没有具体的值
    
            xiaoming.name = "小明";
            xiaoming.age = 17;
            System.out.println(xiaoming.name);
            System.out.println(xiaoming.age);
    
            System.out.println(xiaohong.name);//默认值null
            System.out.println(xiaohong.age);//默认值0
            //由此可见,同一个类new过来的对象是互不影响的,是一个具体的实例
        }
    }
    

    Student类就是一个抽象的模板,然后我们通过new关键字,可以创建不一样的具体的实例!

    大家现在再品一下这句话: 以类的方式组织代码,以对象的组织(封装)数据 ,是不是恍然大悟!

    构造器详解

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

    package com.xiaodi.operator.oop.demo01.demo02;
    
    public class Person {
    
    }
    
    package com.xiaodi.operator.oop.demo01.demo02;
    
    //一个项目应该只存在一个main方法
    public class Application {
        public static void main(String[] args) {
            Person person = new Person();
        }
    }
    

    Person这个类里面我们没有写方法,但是还是能new一个实例出来,就证明类里面有一些默认的东西

    这个对象是怎么来的,为什么能凭空new出来,我们去看这个Person类生成的class文件

    我们在IDEA点开项目架构,选择modules,再选择新增一个目录(Add Content Root),选择我们的out目录,找到对应的class文件打开就行(如果没有对应的class文件,在IDEA运行一下文件就会生成) Person.class文件内容如下:

    //
    // Source code recreated from a .class file by IntelliJ IDEA
    // (powered by FernFlower decompiler)
    //
    
    package com.xiaodi.operator.oop.demo01.demo02;
    
    public class Person {
        public Person() {
        }
    }
    

    你会发现 默认 的帮我们 加了一个方法而且这个方法没有返回值,方法名字和类名相同 ;其实这就是一个构造器。

    得出结论:一个类即使什么都不写,它也会存在一个方法

    • 类中的构造器也称为构造方法,是在进行创建对象的时候必须调用的。并且构造器有以下两个特点:1、必须和类的名字相同2、必须没有返回类型,也不能写void

    在.java文件显示的定义构造器:

    package com.xiaodi.operator.oop.demo01.demo02;
    
    public class Person {
        public Person() {
    
        }
    }
    

    那么有人就会问,这个无参构造器能干什么:

    package com.xiaodi.operator.oop.demo01.demo02;
    
    public class Person {
    
        //给我们初始化一些信息
        String name;
    
        //作用
        //1、使用new关键字,本质是在调用构造器
        //2、用来初始化值
        public Person() {
            this.name = "XiaoDi";
        }
    
        //有参构造:一旦定义了有参构造,无参构造就必须显示定义
        public Person(String name) {
            this.name = name;
        }
    
        //快捷键:Alt+Insert ,选择Constructor能自动生成构造器,参数能选
    }
    
    package com.xiaodi.operator.oop.demo01.demo02;
    
    //一个项目应该只存在一个main方法
    public class Application {
        public static void main(String[] args) {
            //我们可以重载很多个构造器,使用new实例化一个对象的时候,他会根据你传的参数来判断你调用的是哪个构造器
            Person person = new Person();
            Person person1 = new Person("XiaoDi");
    
            System.out.println(person.name);//null
            System.out.println(person1.name);//XiaoDi
        }
    }
    

    构造器总结:

    构造器定义需注意:

    • 和类名相同
    • 没有返回值

    构造器的作用:

    • 使用new关键字的时候构造器就执行了
    • 初始化对象的值

    注意点:

    • 定义了有参构造之后,如果想要使用无参构造,就必须显示的去定义一个无参构造
    • 我们可以重载很多个构造器,使用new实例化一个对象的时候,他会根据你传的参数来判断你调用的是哪个构造器

    快捷键:

    • 在IDEA中使用 Alt+Insert ,选择Constructor能自动生成构造器,参数能选

    构造器必须要掌握

    类与对象简单小结

    1、类与对象

    • 类是一个模板(抽象),对象是一个具体的实例

    2、方法

    • 定义、调用

    3、对象的引用

    • 引用类型、基本类型(8)
    • 对象是通过引用来操作的

    4、属性:也叫字段(Field)、或成员变量

    默认初始化:

    数字:0、0.0;

    char:u0000(转化为int类型输出0);

    boolean:false;

    引用:null

    属性的定义:修饰符 属性类型 属性名 = 属性值;

    5、对象的创建和使用

    • 必须使用new关键字创造对象、构造器 :Person xiaodi = new Person();
    • 对象的属性 xiaodi.name;
    • 对象的方法 xiaodi.sleep();

    6、类

    • 类里面只能写 属性方法

    面向对象三大特性

    封装

    • 该露的露,该藏的藏 :比如遥控器,暴露一些开关换台键就行,一些底层的就藏起来。用户不需要全部了解我们程序设计要追求 “高内聚,低耦合” 。高内聚:就是类的内部数据操作细节自己完成,不允许外部干涉;低耦合:仅暴露少量的方法给外部使用。
    • 封装(数据的隐藏)通常,应禁止直接访问一个对象中数据的实际表示,而应该通过操作接口来访问,这称为信息隐藏
    • 记住这句话就够了: 属性私有,get\set

    听不懂没关系上代码:

    package com.xiaodi.operator.oop.demo03;
    
    //学生类
    public class Student {
    
        //私有属性    private:私有
        private String name; //名字
        private int age; //年龄
        private int id; //学号
        private char sex; //性别
    
        //提供一些可以操作这个属性的方法!
        //提供一些public 的 get、set方法
    
        //get 获取这个数据
        public String getName() {
            return this.name;
        }
    
        //set 给这个数据设置值
        public void setName(String name) {
            this.name = name;
        }
    
        //记住Alt+Insert快捷键然后选择Getter 或者Setter 或者Getter and Serter然后能选择私有属性进行快速生成
    
        //有人就会问了,封装有啥子用(可以避免用户去破坏这个系统,输入一些注入漏洞代码 或 输入一些不符合实际的东西)
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            if (age > 120 || age < 0) { //不合法
                this.age = 3;
            }else {
                this.age = age;
            }
        }
    
    }
    
    package com.xiaodi.operator.oop;
    
    import com.xiaodi.operator.oop.demo03.Student;
    
    public class Application {
        public static void main(String[] args) {
            Student s1 = new Student();
    
            //当属性加上private之后就变成私有了
            //是不能像原来那样调用的;原来那样调用是因为属性是public公共的。
            //s1.name = "xiaodi"; 这样是会报红的
    
            //通过类中公共方法调用、和修改
            //get 访问
            String name = s1.getName();
    
            //set 修改
            s1.setName("晓迪");
    
            //输出看一下
            System.out.println(s1.getName());
    
            //封装的作用
            s1.setAge(999);
            System.out.println(s1.getAge());//不符合就表示用户连基本的尝试都没有估计是个儿童,直接定义为3岁即可
        }
    
    }
    

    总结->封装的意义:

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

    作用在什么地方:这就要通过大家平时多写代码去积累经验了,大家现在还只是学会了这样一个操作,不知道什么地方需要使用到,在我们后面的学习中就会经常去写封装这个东西,大家到时候慢慢积累就行。

    聊一聊方法重载:比如我们的println,为什么它啥都能输出,就是因为它重载了很多方法,方法的参数定义的不同,实现不同类型的输出;大家可以在IDEA中按住Ctrl键然后点击println进去看一下

    继承

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

    上代码理解:

    Person 人 父类

    package com.xiaodi.operator.oop.demo04;
    
    //Person 人 : 父类
    public class Person {
    
        //public 公共的,子类能继承
        //protected 受保护的
        //default 不写,默认
        //private 私有的,子类不能继承;一般属性才会是私有的
        public int money = 10_0000; //公共的
        private int money1 = 10_0000_0000; //私有的,子类就不能继承,但是可以通过封装思想,留一些可以使用这些钱的方法
    
        public void say() {
            System.out.println("说话");
        }
    
        public int getMoney1() {
            return money1;
        }
    
        public void setMoney1(int money1) {
            this.money1 = money1;
        }
    }
    

    Student 学生 子类

    package com.xiaodi.operator.oop.demo04;
    
    //学生也是人 所以继承人 :派生类或子类
    public class Student extends Person {
        //子类可以继承父类的所有方法、以及属性;
    }
    

    Application 启动方法(测试)

    package com.xiaodi.operator.oop;
    
    import com.xiaodi.operator.oop.demo04.Student;
    
    public class Application {
        public static void main(String[] args) {
            Student student = new Student();
            System.out.println(student.money); //父类的属性 (public)
            student.say(); //父类的方法(public)
    
            // System.out.println(student.money1);
            // (private)私有的不能继承,但是可以通过 父类给我留的方法去使用
            System.out.println(student.getMoney1());
        }
    }
    

    继承小总结:

    • 关键字:extends;语法:在类名后面加上:[extends 要继承的类名]
    • 子类继承父类所有方法以及公共属性
    • 私有属性是不能继承的(但是一般情况下是会通过封装的一些方方法去调用或修改);

    Object类

    记住一个快捷键Ctrl+H:查看当前继承的继承情况

    当我们Person类什么都不写的情况下,new一个Person对象,然后输入person.就能点出一些方法,但是我们什么都没定义这是为什么呢?

    package com.xiaodi.operator.oop.demo04;
    
    //Person 人
    //在Java中,所有的类,都默认直接或者间接继承Object
    public class Person /*extends Object*/{
    
    }
    
    package com.xiaodi.operator.oop;
    
    import com.xiaodi.operator.oop.demo04.Person;
    
    public class Application {
        public static void main(String[] args) {
            Person person = new Person();
            //person.(equals(),hashCode(),toString()......)
        }
    
    }
    
    • 在Java中,所有的类,都默认直接或者间接继承Object类 (如果是间接继承,那么Object就是它的爷爷,因为Java中类只有单继承,没有多继承)

    super详解

    • this是代表当前类的
    • super是代表父类的

    父类:Person

    package com.xiaodi.operator.oop.demo04;
    
    //Person 人
    public class Person /*extends Object*/{
        //修饰符 protected 受保护的
        protected String name = "晓迪";
    
        public void print() {
            System.out.println("Person");
        }
    }
    

    子类:Student

    package com.xiaodi.operator.oop.demo04;
    
    //学生也是人 所以继承人 :派生类或子类
    public class Student extends Person {
        private String name = "小迪";
    
        public void print() {
            System.out.println("Student");
        }
    
        public void text(String name) {
            System.out.println(name);//方法里的name
            System.out.println(this.name);//当前类的name
            System.out.println(super.name);//父类的name
        }
    
        public void text1() {
            print();//这个是当前类的print
            this.print();//这个也是当前类的print(建议写法)
            super.print();//父类里的print
        }
    }
    

    启动程序(测试):Application

    package com.xiaodi.operator.oop;
    
    import com.xiaodi.operator.oop.demo04.Student;
    
    public class Application {
        public static void main(String[] args) {
            Student student = new Student();
    
            student.text("XiaoDi");
            System.out.println();//换行符
            student.text1();
        }
    }
    

    输出结果:

    XiaoDi

    小迪

    晓迪

    Student

    Student

    Person

    想一下如果new子类的时候构造器的执行顺序是怎么样的:

    父类:Person

    package com.xiaodi.operator.oop.demo04;
    
    //Person 人
    public class Person /*extends Object*/{
        public Person() {
            System.out.println("Person无参构造执行了");
        }
    }
    

    子类:Student

    package com.xiaodi.operator.oop.demo04;
    
    //学生也是人 所以继承人 :派生类或子类
    public class Student extends Person {
        public Student() {
            super();//子类构造器隐藏代码,在构造器的第一行,这一行不写效果也是一样的
            //this("xiaodi"); 我们要调用自己的有参构造this("xiaodi");,也需要放在第一行,否则报错,也就是说两者在同一个构造器里只能使用一个;
    
            System.out.println("Student无参构造执行了");
        }
        public String name;
    
        public Student(String name) {
            this.name = name;
        }
    }
    

    启动程序(测试):Application

    package com.xiaodi.operator.oop;
    
    import com.xiaodi.operator.oop.demo04.Student;
    
    public class Application {
        public static void main(String[] args) {
            Student student = new Student();
        }
    }
    

    输出:

    Person无参构造执行了

    Student无参构造执行了

    也就是说父类的无参构造器先执行了

    • 如果说我们要调用自己的有参构造this("xiaodi");,也需要放在第一行,否则报错,也就是说两者在同一个构造器里只能使用一个;这个就是super关键字坑爹的地方

    那么假如我的父类里面没有无参构造器:

    父类:Person

    package com.xiaodi.operator.oop.demo04;
    
    //Person 人
    public class Person /*extends Object*/{
        public String name;
    
        public Person(String name) {
            this.name = name;
        }
    }
    

    子类:Student

    package com.xiaodi.operator.oop.demo04;
    
    //学生也是人 所以继承人 :派生类或子类
    public class Student extends Person {
        public Student() {//报错
            System.out.println("Student无参构造执行了");
        }
    
    }
    
    • 会直接导致子类的无参构造也写不了

    但是也不是没有办法,没有无参构造,那你显示的写出调用父类的有参构造就行了:

    子类:Student

    package com.xiaodi.operator.oop.demo04;
    
    //学生也是人 所以继承人 :派生类或子类
    public class Student extends Person {
        public Student() {
            super("小迪");
            System.out.println("Student无参构造执行了");
        }
    
    }
    

    super小总结:

    • 1、super是在子类中调用父类的属性或者方法 例如:super.name; super.print();( 只能出现在子类的方法或构造方法中
    • 2、super调用父类的构造方法,必须在子类的构造方法的第一个(是隐藏代码,写不写效果是一样的)
    • super调用父类构造 和 this调用本类(子类)有参构造 不能同时在一个构造方法里面使用( 也就是说能通过骚操作来解决 ):

    父类:Person

    package com.xiaodi.operator.oop.demo04;
    
    //Person 人
    public class Person /*extends Object*/{
    
        public Person() {
            System.out.println("Person无参构造执行了");
        }
        public String name;
    }
    

    子类:Student

    package com.xiaodi.operator.oop.demo04;
    
    //学生也是人 所以继承人 :派生类或子类
    public class Student extends Person {
        public Student() {//报错
            this(18);
    
            System.out.println("Student无参构造执行了");
        }
        public int age;
    
        public Student(int age) {
            super();
            this.age = age;
        }
    }
    

    如果要调用子类中的有参构造方法的话,且不影响代码,就能像上面这种方法写: 调用子类有参构造的同时还调用了父类的无参构造 ,以上代码的执行顺序是:1父类无参构造、2age=18、3子类无参构造

    super VS this

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

    方法重写

    静态方法

    父类:B

    package com.xiaodi.operator.oop.demo04;
    
    //重写都是方法重写,和属性无关
    public class B {
        public static void test() {
            System.out.println("B->test()");
        }
    }
    

    子类:A

    package com.xiaodi.operator.oop.demo04;
    
    public class A extends B{
        public static void test() {
            System.out.println("A->test()");
        }
    }
    

    启动程序:Application

    package com.xiaodi.operator.oop;
    
    import com.xiaodi.operator.oop.demo04.A;
    import com.xiaodi.operator.oop.demo04.B;
    
    public class Application {
        public static void main(String[] args) {
    
            A.test();
    
            B.test();
        }
    }
    

    输出:

    A->test()

    B->test()

    非静态方法

    父类:B

    package com.xiaodi.operator.oop.demo04;
    
    //重写都是方法重写,和属性无关
    public class B {
        public void test() {
            System.out.println("B->test()");
        }
    }
    

    子类:A

    package com.xiaodi.operator.oop.demo04;
    
    public class A extends B{
        //这里使用Alt+Insert快捷键生成重写选择 有Override的
    
        //Override:重写
        @Override //注解:有功能的注释!这个不用纠结我们后面会单独讲
        public void test() {
            System.out.println("A->test()");
        }
    }
    

    启动程序:Application

    package com.xiaodi.operator.oop;
    
    import com.xiaodi.operator.oop.demo04.A;
    import com.xiaodi.operator.oop.demo04.B;
    
    public class Application {
    
        //静态方法和非静态方法区别很大!
            //静态方法:方法的调用只和左边定义的数据类型有关(这个不叫重写)
            //非静态方法:非静态(且修饰符只能是public):子类重写了父类的方法(这个才叫重写)
        public static void main(String[] args) {
            //
            A a = new A();
            a.test(); //A
    
            //父类的引用指向了子类
            B b = new A();//子类重写了父类的方法
            b.test();//B
        }
    }
    

    方法重写小总结:

    • 首先需要有继承关系,子类重写父类的 非静态 方法!

    特点:

    • 1、方法名必须相同
    • 2、参数列表必须相同 (重载是当前类的,而且参数列表不相同)
    • 3、修饰符:重写时子类的访问控制修饰符不能比父类的范围小(public>protected>default>private)(这个先不要纠结,随着后面的学习再来看这个笔记)
    • 抛出的异常;范围可以被缩小,但不能扩大;(后面再去了解)ClassNotFoundException -->Exception(大)

    重写,子类的方法和父类必须要一致;方法体不同!

    为什么要重写:

    • 1、父类的功能,子类不一定需要,或者不一定满足!

    快捷键:Alt+Insert,然后选择override

    多态

    多态

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

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

    • 多态存在的条件

    • 有继承关系

    • 子类重写父类的方法

    • 父类引用指向子类对象

    • 注意:多态是方法的多态,属性没有多态性

    上代码:

    父类:Person

    package com.xiaodi.operator.oop.demo05;
    
    public class Person {
        public void run() {
            System.out.println("run");
        }
    }
    

    子类:Student

    package com.xiaodi.operator.oop.demo05;
    
    public class Student extends Person{
        @Override
        public void run() {
            System.out.println("son");
        }
    
        public void eat() {
            System.out.println("eat");
        }
    }
    

    启动程序:Application

    package com.xiaodi.operator.oop;
    
    import com.xiaodi.operator.oop.demo05.Person;
    import com.xiaodi.operator.oop.demo05.Student;
    
    public class Application {
    
        public static void main(String[] args) {
    
            //一个对象的实际类型是确定的
            //new Student();
            //new Person();
    
            //可以指向的引用类型就不确定了
            Student s1 = new Student();
            Person s2 = new Student();//父类的引用指向子类
            Object s3 = new Student();
            //得出结论:一个类的实际对象类型是确定的,但是指向的引用类型可以是他的父类和Object类
    
            //我们现在在Person类里写一个run方法,我们就能用s2去调用(输出run);然后我们去子类重写一下这个rum方法
            s2.run();
            s1.run();
            //两个都输出son(也就是说如果子类重写了父类的方法,那么就执行子类的方法)
    
            //我们在子类加了一个eat方法(父类没有):那么s2还能去调用这个方法吗(s1肯定可以,因为s1就是Student类型)
            s1.eat();
            //s2.eat();  是不可以这样去调的(因为我们的s2是Person类型的,person里没有这个方法)
            //但是如果person里面和student里面都有的话,子类没有重写的情况下那么它就调用父类的,如果子类重写了那么就调用子类的
    
            //得出结论:对象能执行哪些方法,主要看对象左边的类型,和右边关系不大
            //        Student(子类)类型能调用的方法都是自己的或者继承父类的
            //        Person(父类)类型,虽然可以指向子类,但是不能调用子类独有的方法
    
        }
    }
    

    多态小总结:注意事项(一定要把注意事项搞明白我们才能避免错误的发生)

    • 1、多态是方法的多态,属性没有多态
    • 2、必须是有父子关系 (如果没有关系转换的话会报一个异常:ClassCastException!)
    • 3、多态存在条件:(1)需要有继承关系 (2)方法需要重写 (3)父类型引用指向子类对象有些方法不能重写:1、static 方法是属于类的,它不属于实例2、final 常量3、private方法是私有的也不能重写

    instanceof和 类型转换(引用类型)

    instanceof

    instanceof:判断一个对象是什么类型(有父子关系就ok,没有就不行)

    演示:

    父类:Person

    package com.xiaodi.operator.oop.demo05;
    
    public class Person {
        public void run() {
            System.out.println("run");
        }
    }
    

    子类:Student

    package com.xiaodi.operator.oop.demo05;
    
    public class Student extends Person{}
    

    子类:Teacher

    package com.xiaodi.operator.oop.demo05;
    
    public class Teacher extends Person {}
    

    启动程序:Application

    package com.xiaodi.operator.oop;
    
    import com.xiaodi.operator.oop.demo05.Person;
    import com.xiaodi.operator.oop.demo05.Student;
    import com.xiaodi.operator.oop.demo05.Teacher;
    
    public class Application {
    
        public static void main(String[] args) {
            //当前的关系:Opject > Person > Student
            //当前的关系:Opject > Person > 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
            System.out.println("=====================");
    
            Person person = new Student();
            System.out.println(person instanceof Student);//true
            System.out.println(person instanceof Person);//true
            System.out.println(person instanceof Object);//true
            System.out.println(person instanceof Teacher);//false
            // System.out.println(person instanceof String);//编译报错!
            System.out.println("=====================");
    
            Student student = new Student();
            System.out.println(student instanceof Student);//true
            System.out.println(student instanceof Person);//true
            System.out.println(student instanceof Object);//true
            // System.out.println(student instanceof Teacher);//编译报错!
            // System.out.println(student instanceof String);//编译报错!
    
        }
    }
    

    得出结论:System.out.println(x instanceof y);

    • 能不能编译通过主要看x的引用类型跟y是否存在父子关系(如果存在编译通过,如果不存在编译不通过)
    • 而结果主要看x指向的实际类型是不是y的子类型(就是说x指向的类型跟y有关系就为true)

    类型转换

    父类:Person

    package com.xiaodi.operator.oop.demo05;
    
    public class Person {
        public void run() {
            System.out.println("run");
        }
    }
    

    子类:Student

    package com.xiaodi.operator.oop.demo05;
    
    public class Student extends Person{
        public void go() {
            System.out.println("go");
        }
    }
    

    启动程序:Application

    package com.xiaodi.operator.oop;
    
    import com.xiaodi.operator.oop.demo05.Person;
    import com.xiaodi.operator.oop.demo05.Student;
    
    public class Application {
        public static void main(String[] args) {
            //现在我们知道Student里面能用两个方法,一个是go,一个是继承过来的run
            //类型之间的转换 :父(高) 子(低)
            //低转高 (子类转换为父类:可能丢失自己本来的一些方法!)
            Student student = new Student();
            Person ps = student;
            ps.run();
    
            Person person = new Student();
            //我们这边person.go();是会报错的,如果我们想让他执行就要进行类型转换
            //高转低(强制转换)Person类型转换成Student类型
            Student person1 = (Student) person;
            person1.go();
            //和成一句代码就是象下面这样
            ((Student) person).go();
    
        }
    }
    

    多态总结:

    • 1、父类引用指向子类的对象
    • 2、把子类转换为父类,向上转向
    • 3、把父类转换为子类,向下转型;强制转换
    • 4、方便方法的调用,减少重复的代码!使代码变简洁!

    这些大家可能会很懵,都是一些理论知识,每个人的理解能力不一样,知道怎么用就行,等后面用到了,你自然就懂了,后面自己慢慢悟,别着急!别着急!别着急!

    static关键字详解

    static:加在属性上叫静态属性,加在方法上叫静态属性

    package com.xiaodi.operator.oop.demo06;
    
    public class Student {
        private static int age; //静态属性
        private double score; //非静态属性
    
        public void run() {} //非静态方法
    
        public static void go() {} //静态方法
    
        public static void main(String[] args) {
            Student s1 = new Student();
    
            System.out.println(Student.age);//通过类调用
            System.out.println(age);//跟上面的效果一样
            //System.out.println(score);//非静态属性会报错
            System.out.println(s1.age);//通过对象调用
            System.out.println(s1.score);//通过对象调用
    
            Student.go();
            go();
            s1.go();
            s1.run();
        }
    }
    

    一个类里面的静态属性或方法在内存中是和类一起加载的,能通过类调用(在本类中甚至能直接写);非静态的则需要通过new

    代码块(补充)

    package com.xiaodi.operator.oop.demo06;
    
    public class Person {
        //匿名代码块
        {
            System.out.println("匿名代码块");
        }
    
        //静态代码块
        static {
            System.out.println("静态代码块");
        }
    
        //构造器
    
        public Person() {
            System.out.println("构造方法");
        }
    
        public static void main(String[] args) {
            Person person1 = new Person();
            System.out.println();
            Person person2 = new Person();
        }
    }
    

    执行结果:

    静态代码块

    匿名代码块

    构造方法

    匿名代码块

    构造方法

    通过执行结果我们发现: 静态代码块最优先,其次才是匿名代码块,最后才执行构造方法,且静态代码块只执行一次

    匿名代码块我们一般用来赋一些初始值

    静态导入包(一般人很少这么玩,知道有这个东西就行)

    package com.xiaodi.operator.oop.demo06;
    
    //import java.lang.Math.random; 导入java.lang下面的Math类下面的random方法,这样是会报错的,需要加上静态
    import static java.lang.Math.random;
    
    public class Test {
    
        public static void main(String[] args) {
            //生成随机数:Math.random
            System.out.println(Math.random()); //这样调用是不是很麻烦,我们把方法导进来
            System.out.println(random());//导进来之后就能这样写
        }
    }
    

    主要说一下: 通过final修饰的类就不能被继承了 (意思就是断子绝孙了哈哈哈)

    抽象类和接口

    抽象类

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

    父类:Action

    package com.xiaodi.operator.oop.demo07;
    
    //abstract 抽象类
    public abstract class Action {
    
        //abstract 抽象方法,只有方法的名字,没有方法的实现 (约束,让别人帮我们实现)
        public abstract void doSomething();
    
        //抽象类的特点:
            //不能new这个抽象类,只能靠子类去实现它:约束!
            //只能去new它的子类,如果它的子类没实现,只能去new它的子子类
    
            //抽象类里面可以写普通方法
            //抽象方法必须在抽象类中
    
        //思考?
        //       抽象类既然不能new,那么存在构造器吗?(大家自己打开class编译看一下)
        //       抽象类存在的意义? (提高开发效率)
    
    }
    

    子类:A

    package com.xiaodi.operator.oop.demo07;
    
    //抽象类的所有方法,继承它的子类,都必须要重写去实现他的方法,除非它的子类也是抽象类
    //抽象类extends:单继承,有局限性,(接口可以多继承)
    public class A extends Action{
        @Override
        public void doSomething() {
            System.out.println("doSomething");
        }
    }
    

    抽象类并不是我们的重点,了解一下就好了; 下面的接口跟抽象类非常像用的人也比较多(接口可多继承)

    接口

    • 普通类:只有具体实现
    • 抽象类:具体实现和规范(抽象方法)都有! 业余约束!
    • 接口:只有规范!自己无法写方法~专业的约束! 能干约束和实现分离:面向接口编程
    • 接口就是规范,定义的是一组规则,体现了现实世界中“如果你是...则必须能...”的思想。如果你是天使,则必须能飞。如果你是汽车,则必须能跑。如果你是好人,则必须干掉坏人;如果你是坏人,则必须欺负好人。
    • 接口本质是契约,就像我们人间的法律一样。制定好后大家都遵守。
    • OO的精髓,是对对象的抽象,最能体现这一点的就是接口。为什么我们讨论设计 模式都只针对具备了抽象能力的语言(比如c++、java、c#等),就是因为设计模式所研究的,实际上就是如何合理的去抽象。

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

    接口:UserService

    package com.xiaodi.operator.oop.demo08;
    
    //interface 定义的关键字  接口都需要有实现类
    public interface UserService {
        //接口中的所有定义其实都是抽象的
    
        //属性默认的是一个静态常量 public static final
        int age = 99; //一般不会这么玩
    
        //  public abstract void add(String name);//假设方法修饰符你不写默认就是public abstract
        //所以接口都可以像下面这样去写 返回值类型 方法的名字([参数可选]);
        void add(String name);
        void delete(String name);
        void update(String name);
        void query(String name);
    }
    

    接口:TimeService

    package com.xiaodi.operator.oop.demo08;
    
    public interface TimeService {
        void timer();
    }
    

    接口的实现:UserServiceImpl

    package com.xiaodi.operator.oop.demo08;
    
    //实现类名一般加一个Impl就行
    //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、定义一些方法,让不同的人实现(比如你有10个员工,但是他们都会去完成一份共同的工作,10个人实现的是一个接口,但是有10种不同是实现方式)
    • 3、方法都是:public abstract 属性都是:public static final,一般没人会去玩属性
    • 4、接口不能被实例化~接口中没有构造方法
    • 5、可以通过implements实现多个接口
    • 6、实现接口必须重写接口里面的方法

    抽象的思想很难锻炼 :Java谁都会对吧 那么为什么有些人能做到架构师有些人不能 (架构师就需要抽象思维非常非常好,你要把一个系统的结构全部抽象成接口,你能通过接口去定义一个系统的时候,那时你的架构能力就已经很强了,但是大部分人做不到)

    内部类

    • 内部类就是在一个类的内部在定义一个类,比如,A类中定义了一个B类,那么B类相对A类来说就称为内部类,而A类相对B类来说就是外部类。
    • 1、成员内部类
    package com.xiaodi.operator.oop.demo09;
    
    public class Outer {
    
        private int id = 10;
        public void out() {
            System.out.println("这是外部类的方法");
        }
    
        public class Inner{
            public void in() {
                System.out.println("这是内部类的方法");
            }
            //内部类访问外部类的私有属性
            public void getId() {
                System.out.println(id);
            }
        }
    }
    

    测试:Application

    package com.xiaodi.operator.oop;
    
    import com.xiaodi.operator.oop.demo09.Outer;
    
    public class Application {
        public static void main(String[] args) {
    
            //外部类的实例化大家都懂了吧
            Outer outer = new Outer();
    
            //这个内部类要通过外部类来实例化
            Outer.Inner inner = outer.new Inner(); //是不是非常奇葩
            inner.in();
            inner.getId();
        }
    }
    
    • 2、静态内部类
    package com.xiaodi.operator.oop.demo09;
    
    public class Outer {
    
        private int id = 10;
        public void out() {
            System.out.println("这是外部类的方法");
        }
    
        public static class Inner{
            public void in() {
                System.out.println("这是内部类的方法");
            }
    
            //外部的id就拿不到了,因为是静态的类存在的时候就存在,那个时候id这个属性还没出生
        }
    }
    
    • 3、局部内部类
    package com.xiaodi.operator.oop.demo09;
    
    public class Outer {
    
        //局部内部类 写在方法里面的
        public void method() {
            class Inner {
                public void in() {
    
                }
            }
        }
    }
    
    • 4、匿名内部类
    package com.xiaodi.operator.oop.demo09;
    
    public class Outer {
        public static void main(String[] args) {
            //我们正常实例化是像下面这样干的
            Apple apple = new Apple();
            //匿名内部类就是没有名字去初始化类,不用把实例保存到变量中
            new Apple().eat();
    
            //匿名内部类 接口的实现
            new UserService() {
                @Override
                public void hello() {
    
                }
            };
        }
    
    }
    class Apple{
        public void eat() {
            System.out.println("1");
        }
    }
    
    interface UserService {
        void hello();
    }
    
    • 5、也叫内部类
    package com.xiaodi.operator.oop.demo09;
    
    public class Outer {
    
    }
    //一个java文件中可以有多个class类,但是只能有一个public class类
    class A {
    
    }
    

    大家这里先了解一下就可以,讲内部类主要目的是让大家知道有这个东西,以后分析源码看到,不要不知道这是什么东西就行

    本章内容较多,建议看两遍以上,第二遍你会发现一些第一遍不理解的,慢慢理解了

    相关文章

      网友评论

          本文标题:Java面向对象编程

          本文链接:https://www.haomeiwen.com/subject/ueicjrtx.html