Java的面向对象编程完全指南:从概念到实战

编程如同修炼武功,面向对象就是你的九阳神功。练成此功,万般代码皆可破!现在,让我们正式踏上这场OOP的修行之旅吧! 

目录

📢 文章提示

🌟 文章前言

一、首先我们来谈谈面型对象和面向过程的区别

面向过程 vs 面向对象

二、面向对象核心概念 

类与对象的关系

创建与初始化对象

构造器

this关键字 

 实体类(JavaBean)

static修饰成员变量

static修饰方法

工具类与静态方法

静态方法与实例方法访问注意事项

三、面向对象三大特性

封装

封装的设计要求

继承 

1、extends

2、权限修饰符

3、继承的特点 

 4、方法重写(声明不变,重新实现)

 5、重写toString方法

 6、子类构造器super(...)调用父类构造器(调用父类成员)

7、子类构造器this(...)调用兄弟构造器(实现填写默认信息) 

多态

1、多态前提:有继承/实现关系;存在父类引用子类对象;存在方法重写

2、多态的好处

3、什么不能被重写

4、多态下的类型转换(instanceof判断)

🚀 文章总结


📢 文章提示

  1. 本文包含完整代码示例图解说明

  2. 学习前需要掌握Java基础语法

  3. 建议配合IDE边看边实践J

🌟 文章前言

面向对象编程(OOP)是现代编程的基石,掌握它就像获得打开编程世界的万能钥匙。本文将通过生动比喻+实战代码,带你从零构建OOP知识体系。无论你是刚入门的新手,还是需要巩固基础的中级开发者,这里都有你需要的干货!


一、首先我们来谈谈面型对象和面向过程的区别

面向过程 vs 面向对象

面向过程面向对象
思维怎么做(How)谁来做(Who)
特点线性思维模块化思维
适用场景简单任务(计算器)复杂系统(电商平台)
示例做菜:洗菜→切菜→炒菜→装盘开餐厅:厨师、服务员、收银员分协作

Q: 谈谈面向对象和面向过程的理解

S:面向过程:就像你自己动手做一切,从头到尾一步步按顺序来。面向对象:更像是管理一个团队,给不同的人分配不同的任务,然后大家各司其职共同完成目标。

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

二、面向对象核心概念 

类与对象的关系

是一种抽象的数据类型,它定义了某一类事物的属性(字段)和行为(方法)。类是对现实世界实体的一种描述或蓝图,并不表示具体的实体。

例如,Person类可以包含人的姓名、年龄等属性以及走路、说话等行为

对象是类的具体实例,代表了现实中某个具体的实体。例如,“张三”就是Person类的一个具体实例,具有特定的名字和年龄等属性值。

代码体现:

//  类定义(设计图)
class Phone {
    // 属性(状态)
    String brand;
    double price;
    
    // 方法(行为)
    void call(String number) {
        System.out.println("正在拨打:" + number);
    }
}

public class Main {
    public static void main(String[] args) {
        //  创建对象(根据设计图生产产品)
        Phone myPhone = new Phone();
        myPhone.brand = "华为";
        myPhone.price = 5999.0;
        myPhone.call("10086");
    }
}

创建与初始化对象

在Java中,创建对象通常使用new关键字。这个过程不仅分配了内存空间,还会调用相应的构造器来初始化对象的状态。

内存分配

java在内存的JVM虚拟机上运行,JAVM虚拟机又分为堆内存、栈内存和方法区一同执行程序。堆是存放对象的地方,栈是存放方法的地方,方法区是放类文件的地方。

        变量存在栈里,变量指向对象,对象存在堆里,对象指向类,类存在方法区,将方法区中的方法调到栈中执行
万物皆对象,一个数据由一个对应的对象处理,我们设计对象时就是在设计类(对象的模板)。

构造器

(1)、构造器:类中定义的方法,用来初始化对象,在类中定义的方法,称为方法,在类中定义的变量,称为属性。名字与类名一致,无返回值,无参数,无返回值类型,无访问修饰符

(2)、特点:创建对象时,对象会自动调用构造器,如果没有定义构造器,JVM会自动生成一个无参构造器。

(3)、应用场景:创建对象时,调用构造器,立即初始化对象成员变量的值。

(4)、注意:类默认有一个无参构造器(没有显示而已),若你自己定义了有参构造器,那么JVM就不会自动生成一个无参构造器。

有参构造:一旦定义了有参构造,无参就必须显示定义

public class User {
    //构造器,特殊方法,不能写返回值,名称与类名一致。
    public User() {System.out.println("==无参构造器执行了==");
    }//无参构造器
    public User(String name, Integer age, boolean gender, String email, String password, Integer math, Integer chinese) {
        this.name = name;
        this.age = age;
        this.gender = gender;
        this.email = email;
        this.password = password;
        this.math = math;
        this.chinese = chinese;
    }//有参构造器,构造器重载
}
//在mian方法中调用构造器
public class main() {
    public static void main(String[] args) {
        User user = new User();//无参构造器返回对象
        user.setName("Rasion");//传入数据给无参构造器对象
        user.setAge(20);
        user.setGender(true);
        user.setEmail("rasion@gmail.com");
        user.setPassword("123456");
        user.setMath(100);
        user.setChinese(100);
        User s2 = new User("Rasion", 20, true, "rasion@gmail.com", "123456", 100, 100);
        //有参构造器返回对象,创建对象时,调用构造器,立即初始化对象。
    }
}
this关键字 

(1)、this 关键字:是一个变量,可以用在方法中,用来拿到当前对象;哪个对象调用方法,this就指向那个对象,也就是哪个对象。

(2)、应用场景:解决变量名称冲突问题。(见有参构造器)。

public void print(String name){//(二)、2、this关键字解决变量冲突问题
    System.out.println(name+this.name);//this.name拿到的是对象变量(成员变量)name,而不是局部变量name
}
 实体类(JavaBean)

(1)、实体类:只能用来封装数据的类,也叫JavaBean。

(2)、特点:类中成员变量私有,提供getter和setter方法;类中需要一个无参构造器,和有参构造器可选

(3)、应用场景:实体类的对象只负责数据的封装,不涉及任何业务逻辑。而数据的业务处理交给其他类的对象来完成,以实现数据和业务处理相分离(解耦)。

static修饰成员变量

(1)、static关键字:修饰成员变量、方法、类,修饰后,该成员变量、方法、类属于类,而不是对象。

(2)、静态变量(类变量):有static修饰,属于类,被类的全部对象共享,所有对象都可以访问。

(3)、实例变量(对象的变量):没有static修饰,属于每个对象,每个对象都有自己的变量,对象可以访问。

(4)、应用场景:如果某一个数据只需要一份,并且希望能够被共享、访问、修改,则该数据被定义成静态变量。 (如用户类,记录了创建了多少个用户对象)

public class User {
    static String name;//静态变量(类变量)
    int age;//实力变量(对象的变量)
}
public class main(){
    public static void main(String[] args) {
        User s1= new User();//s1对象
        User s2= new User();//s2对象
        //其中,s1和s2各自都有各自的age变量,但s1不能访问s2的age。
        //但name是静态变量,所有对象都可以访问,存储在类中的。
        //所以我们访问静态变量一般都直接用  类名.静态变量 如 User.name
        User.name="Rasion";
        s1.age=10;
        s1.name="Rasion1";
        s2.age=20;
        s2.name="Rasion2";
        System.out.println(User.name+s1.age+s1.name+s2.age+s2.name);
        //Rasion210Rasion220Rasion2
    }
}
static修饰方法

(1)、static修饰静态方法:有static修饰的成员方法,属于类,最好用类名调用,少用对象名调用。

(2)、实例方法:无static修饰的成员方法,属于对象,用对象名调用,不能用类名调用。

规范:如果一个方法只是为了做一个功能且不需要直接访问对象的数据,这个方法直接定义为静态方法。
如果这个方法是关于对象的行为,需要访问对象的数据,这个方法必须定义为实例方法

(3)、比如在main方法中,我们直接调用其他方法,是用类名调用,只不过类名在同一类中可以忽略不写。(main方法是静态方法)

工具类与静态方法

(1)、工具类:封装了多个静态方法,每个方法用来完成一个功能,给开发人员直接使用。

(2)、区别:实例方法需要创建最想来调用,此时对象占用内存,而静态方法不需要,可以减少内存消耗;静态方法可以用类名调用,调用方便,能节省内存。

public class StaticAbout {//工具类
    //工具类没有创建对象的需求,建议讲工具类的构造器私有。
    private StaticAbout() {
    }
    public static String getCode(int n) {//静态方法与工具类
        String code = "";
        for (int i = 0; i < n; i++) {
            int type = (int) (Math.random() * 3);//0-9 1-26 2-26
            switch (type) {
                case 0:
                    code += (int) (Math.random() * 10);
                    break;
                case 1:
                    code += (char) (Math.random() * 26 + 'a');//得到小写字母的区间
                    break;
                case 2:
                    code += (char) (Math.random() * 26 + 'A');
            }
        }
        return code;
    }
}
静态方法与实例方法访问注意事项

(1)、静态方法中可直接访问静态成员,不可直接访问实例成员。

(2)、实例方法中即可直接访问静态成员,也可直接访问实例成员。

(3)、实例方法中可以出现this关键字,静态方法中不可出现this关键字。

public class Attention{
    public static int count=100;//静态变量
    public static void print(){//静态方法
        System.out.println("Hello World!");
    }
    public String name;//实例变量,属于对象
    public void prints(){//实例方法,属于对象
    }
    public static void main(String[] args) {

    }
    //(1)、静态方法中可直接访问静态成员,不可直接访问实例成员。
    public static void printTest1(){
        System.out.println(count);
        print();
        //System.out.println(name);//报错
        //prints();//报错
        //System.out.println(this);//报错,this代表的只能是对象
        System.out.println(Attention.count);
    }
    //(2)、实例方法中即可直接访问静态成员,也可直接访问实例成员。
    public void printTest2(){
        System.out.println(count);
        print();
        System.out.println(name);
        prints();
        System.out.println(this);//实例方法中,this代表的是当前对象
    }
}

三、面向对象三大特性

面向对象编程(OOP)的核心在于其三大特性:封装、继承和多态。这些概念帮助我们创建更加模块化、易于维护和扩展的代码。

封装

封装是将数据(属性)和操作数据的方法捆绑在一起,并隐藏对象内部的具体实现细节,只暴露必要的接口给外部使用。

        高内聚低耦合:鼓励将相关功能放在一起,减少不同部分之间的依赖。

        属性私有化与get/set方法:通过将类的属性设置为私有,并提供公共的getset方法来访问和修改这些属性,可以控制对属性的访问权限,增加安全性。

封装的设计要求

(1)、封装的设计要求:合理隐藏、合理暴露

(2)、(合理隐藏使用private关键字封装变量,防止用户在其他类中随意对本类内的变量修改数据,只允许在本类中直接被访问。

(3)、(合理暴露使用get和set方法,封装变量,让用户在类外直接调用,修改数据。

public class Person {
    private String name;
    private int age;

    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public int getAge() { return age; }
    public void setAge(int age) { this.age = age; }
}

继承 

1、extends

继承关键字:extends,可以让一个类与另一个类建立起父子关系

public calss B extends A{}//其中A是父类,B是子类

只能继承父类的非私有成员(成员变量、成员方法)

继承后的创建:子类的对象是由子类、父类共同完成的,所以子类对象是完整的,父类对象是部分。

继承的好处:代码重用,减少代码量,提高效率。

2、权限修饰符

用来限制类中的成员能够被访问的范围。

修饰符

本类里

同一个包中的其他类

子孙类(包括不同包的)

任意类

private

缺省

protected

public

public class Father {
    private void privateMethod() {//二、2、权限修饰符之私有方法
        System.out.println("private method");
    }

    void defaultMethod() {//二、2、权限修饰符之缺省方法
        System.out.println("default method");
    }

    protected void protectedMethod() {//二、2、权限修饰符之受保护方法
        System.out.println("protected method");
    }

    public void publicMethod() {//二、2、权限修饰符之公共方法
        System.out.println("public method");
    }
    public static void main(String[] args) {//本类中可以调用的所有方法
        Father father = new Father();
        father.privateMethod();
        father.defaultMethod();
        father.protectedMethod();
        father.publicMethod();//所有类都可以调用public方法
    }
}
//以下为同一包的子类
class SonA extends Father {
    public void test() {
        //privateMethod();//子类中不可以访问父类的私有方法
        defaultMethod();//子类中可以访问父类的缺省方法
        protectedMethod();//子类中可以访问父类的受保护方法
        publicMethod();//子类中可以访问父类的公共方法
    }
}
//以下为不同包的子类
class SonC extends Father {
    public void test() {
        //privateMethod();//子类中不可以访问父类的私有方法
        //defaultMethod();//不同包的子类中可以访问父类的缺省方法
        protectedMethod();//不同包的子类中可以访问父类的受保护方法
        publicMethod();//不同包的子类中可以访问父类的公共方法
    }
}
//以下为不同包的非子类
class main {
    public static void main(String[] args) {
        Father father = new Father();
        //father.privateMethod();
        //father.defaultMethod();
        //father.protectedMethod();
        father.publicMethod();//其他包中,只能访问public方法
    }
}
3、继承的特点 

单继承

一个类只能继承一个直接父类

多层继承

不支持多继承,但支持多层继承(多继承的话若两个父类出现相同方法,无法判断调用哪个)

祖宗类

Java中的所有类都是Object类的子类(一个类要么默认继承Object,要么间接继承)

就近原则

优先访问自己类中,自己类中的没有才访问父类(访问父类的成员要加super)

     4、方法重写(声明不变,重新实现)

    子类重写父类的方法,子类对象调用父类方法,实际调用子类重写的方法。(声明不变,重新实现

    子类重写时,访问权限必须大于或等于父类的该方法的权限(public>protected>default>private)

    重写的方法,必须与重写前的方法具有相同的参数列表和返回值类型(返回值类型一样或更小)。

    私有方法(不能被继承所以不能被重写)和静态方法(自己调用)不能被重写,否则报错。

    public class TestOverride {
        public static void main(String[] args) {//二、4、方法重写
             animal a=new cat();
    //用父类申明对象,利用多态性和向上转型的概念,以实现更加灵活和可扩展的程序设计,降低代码的耦合性
             a.eat();
             cat b=new cat();
             b.eat();
        }
    }
    class animal{
        public void eat(){System.out.println("animal eat");}
    }
    class cat extends animal{
        @Override//方法重写的校验注解(标志),要求重写的方法与父类方法签名一致,否则编译报错,可读性好
        public void eat(){System.out.println("cat eat!!!");}
    }
     5、重写toString方法
    public class TestOverride {
        public static void main(String[] args) {
             animal a=new animal();
             System.out.println(a);
    //若没重写toString方法,则返回:com.rasion.extendANDpolymorphism.extend.cat@4e50df2e
             System.out.println(a.toString());
            //我们直接输出对象,会默认调用Object的toString方法,返回对象的地址信息
            //故我们可以重写toString方法,返回我们想要的信息
        }
    }
    class animal{
        private String name;
        @Override
        public String toString() {//重写toString方法,返回我们想要的信息
            return "animal{" +
                    "name='" + name + '\'' +
                    '}';
        }
    }
     6、子类构造器super(...)调用父类构造器(调用父类成员)

    子类构造器,必须先调用父类的构造器,再调用自己的构造器。(可以用super(null)指定调用父类的有参构造器)

    子类构造器第一行默认都是super(),他会调用父类的无参构造器,若父类没有无参构造器,则报错。

    应用场景:有一些参数是处在父类中,子类没有,但子类需要用到,这时,子类构造器中,可以先调用父类的有参构造器,再调用自己的构造器。

    public class TestOverride {
        public static void main(String[] args) {
            animal a=new cat("小猫","可爱");
            System.out.println(a);//返回:cat{name='小猫', cute='可爱'}
        }
    }
    class animal{
        private String name;
        public animal(){}
        public animal(String name){//父类的有参构造器
            this.name=name;
        }
        protected String getName() {return name;}//父类的getter和setter,方便子类调用
        protected void setName(String name) {this.name = name;}//我认为父类的参数最好只允许子类访问
    }
    class cat extends animal{
        private String cute;
        public cat(){}
        public cat(String name,String cute){//子类有参构造器
            super(name);//===调用父类的有参构造器===
            this.cute=cute;
        }
        @Override
        public String toString() {//只需要重写toString方法,返回我们想要的信息
            return "cat{" +
                    "name='" + getName() + '\'' +
                    ", cute='" + cute + '\'' +
                    '}';
        }
    }
    7、子类构造器this(...)调用兄弟构造器(实现填写默认信息) 

    一般调用兄弟构造器,可以实现代码复用。

    注意:super(..) this(...)必须写在构造器第一行,并且不能同时出现。(兄弟构造器只需要一个调用父类构造器即可)

    public class User{
        private String name;
        private int age;
        private String school;
        //...此处省略get、set方法toString方法
        public User(String name, int age) {
            this(name, age, "CTGU");//调用本类其他构造器,以实现填写默认信息
        }
        public User(String name, int age, String school) {
            this.name = name;
            this.age = age;
            this.school=school;
        }
    }

    多态

    1、多态前提:有继承/实现关系;存在父类引用子类对象;存在方法重写

    多态是在继承/实现的情况下的一种现象、表现为:对象多态、行为多态(对象的多样性,行为的多样性,但是没有成员变量的多态性)

        people p1=new student();//对象多态
        p1.run();//行为多态
        people p2=new teacher();//对象多态
        p2.run();//方法:编译看左边,运行看右边
            
        System.out.println(p2.name);//成员变量:编译看左边,运行也看左边
    2、多态的好处

    右边的对象是解耦合的,更便于扩展和维护。父类类型的变量作为参数可以接收一切子类变量

    问题:多台下不能调用子类独有功能。

    public class TestPolymorphism {
        public static void main(String[] args) {
            people p1=new student();//多态调用不了子类的独有功能
            people p2=new teacher();
            show(p1);
            show(p2);
            System.out.println(p1.name);//成员变量:编译看左边,运行也看左边
        }
        public static void show(people p) {
            //父类类型作为参数,可以接收一切子类变量,不能为Student或者teacher
            System.out.println("=====+++=====");
            p.run();//调用方法:编译看左边,运行看右边
        }
    }
    3、什么不能被重写
    • static 方法 属于类 他不属于实例
    • final 常量
    • private 方法
    4、多态下的类型转换(instanceof判断)

    instanceof (类型转换) 引用类型 判断一个对象是什么类型 如果匹配进行类型转换

    强制类型转换:子类 对象名=(子类) 父类对象名;

    强制类型转换后能够调用子类的私有方法

    在运行时,如果发现对象的正式类型与强制转换后的类型不同,就会报类型异常(ClassCastException)的错误

    在强转前可以用instanceof关键字判断对象的真实类型,再进行强转

    • 父类引用指向子类的对象
    • 把子类转换为父类 向上转型
    • 把父类转换为父子类 向下转型 强制转换
    • 方便方法的调用 减少重复代码
    public class TestPolymorphism {
        public static void main(String[] args) {
            people p1=new student();
            student s=(student)p1;//强制类型转换
    //        teacher t=(teacher)p1;//转换错误,因为p1是student类型
            //编译阶段有类型强转不会报错,运行阶段会报错
        }
        public static void show(people p) {
            if(p instanceof teacher) {//判断类型,一般会在方法中写,来判断p是否为teacher类型
                teacher t=(teacher)p;
                ...//调用teacher的独有功能
            }else if(p instanceof student) {
                student s=(student)p;
                ...
            }
        }
    }

    🚀 文章总结

    面向对象编程就像组建一支分工明确的团队。每个类都是不同岗位的员工:

    • 封装:财务部管钱,其他部门不能直接动保险箱

    • 继承:新员工继承老员工的经验,还能发展新技能

    • 多态:同样汇报工作,程序员用代码演示,产品经理用PPT讲解

    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

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

    抵扣说明:

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

    余额充值