当前位置: 首页 > news >正文

JAVA面向对象2(三大特征)

面向对象的三大特征:封装、继承、多态

封装:

封装,英文单词Encapsulation。

广义的角度来说,将一块经常要使用的代码片段,定义到方法中,是封装。将多个方法和多个状态数据定义到类体中,也是一种封装。

狭义的角度来说,java的封装,就是把类的属性私有化(private修饰),再通过公有方法(public)进行访问和修改

属性封装:

只需要使用访问权限修饰词private来修饰即可

public class Person {
   String name;
   private int age;        // 将属性私有化起来,不让外界直接访问

1:  为什么要封装成员变量(属性)
      因为外界直接访问成员变量,可能会进行修改,也就会可能发生修改的值不合理。比如调用者将人的年龄这个属性可以设置10000。


2: 但是外界可能还是需要修改或者访问成员变量的。 一旦私有化成员变量,外界不能做到访问和修改。 那么如何处理呢?
       为成员变量提供public修饰的get/set方法。 为了防止外界设置的值可能不合理,可以在这些方法中进行限定。

  get方法:  用于获取成员变量的值
            public 返回值类型  getName(){        //get+属性的大驼峰命名法(-Bean)
                return this.成员变量
            }
            -  方法名get后是成员变量的大驼峰命名规则


  set方法:  用来修改成员变量的值
           public  void  setName(String name){
               this.name = name;
           }
           - 方法名set后是成员变量的大驼峰命名规则

  // 给要访问的属性,设置对应的  setter/getter 方法
   public void setAge(int age) {

        if (age >= 0 && age <= 120) {
              this.age = age;
           }

   }

   public int getAge() {
      return this.age;
   }
}

 单例设计模式:

设计模式 是一套被反复使用、多数人知晓的、经过分类编目的代码设计的经验的总结。

使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。

单例模式是一种常用的软件设计模式,属于创建型模式之一。它的目的是确保一个类只有一个实例,并提供一个全局访问点。

即:整个应用程序中,该类型的对象只有一个。

使用场景:

  • 频繁创建和销毁的对象:如果对象创建和销毁的成本较高,且在程序运行期间需要频繁访问,使用单例模式可以提高效率。
  • 控制资源访问:例如,数据库连接、日志对象、配置管理器等,这些资源通常希望在整个应用中只有一份实例。
  • 工具类:对于一些工具类,如缓存、对话框、注册表设置等,使用单例模式可以简化代码,避免重复实例化。

单例模式有两种设计方式: 饿汉模式和懒汉模式

单例饿汉模式:

简单理解:着急创建那个唯一的对象(*.java文件被加载到方法区是就创建该对象)

过程:

1.提供一个private权限的静态当前类属性,并在静态代码段中进行实例化。

2.构造方法私有化,杜绝从外界通过new的方式实例化对象的可能性。(保证这个对象只有一个,外界不能创建新的对象)

3.提供一个public权限的静态方法,获取唯一一个当前类的对象


public class Boss {
   // 1、设计一个私有的、静态的、当前类的对象
   private static Boss instance;
   static {
      // 对instance静态对象进行实例化   静态代码区 加载到方法区是就创建对象 只运行一次
      instance = new Boss();
   }
   
   // 2、将单例类的构造方法私有化,杜绝从外界通过new的方式实例化对象的可能性。
   private Boss() {
      System.out.println("一个Boss对象出现了 "); 
   }

   // 3、需要提供一个public权限的静态方法,可以获取一个当前类的对象。
   public static Boss getCurrentBoss() {
      return instance;
   }
}

主函数里:
    //使用==来判断引用变量里的地址是否时同一个地址(同一个对象)
    Boss i1=Boss.getCurrentBoss();
    Boss i2=Boss.getCurrentBoss();
    System.out.println(i1==i2);    //地址相同,即对象只创建了一个

单例懒汉模式:

简单理解:不着急创建唯一对象 ,不需要在加载期间就创建对象,第一次访问时才创建对象。

过程:

1.提供一个private权限的静态当前类属性。

2.构造方法私有化,杜绝从外界通过new的方式实例化对象的可能性。(保证这个对象只有一个,外界不能创建新的对象)

3.提供一个public权限的静态方法,负责创建单例对象:

        第一次调用时会初始化单例,后续调用则直接返回以及存在的单例。


public class Master {

//1.提供一个private权限的静态当前类属性。

   private static Master instance;
 // 2.构造方法私有化 
   private Master() {
      System.out.println("一个Chairman对象被实例化了 "); 
   }
   
   public static Master getMaster() {
       //3.访问时才初始化
      // 使用到instance对象的时候,判断是不是null,instance里面存储地址
      if (instance == null) {
         // 实例化
         instance = new Master();
      }
      return instance;//第一次调用时会初始化单例,后续调用则直接返回以及存在的单例。
   }
}
主函数里:
    使用==来判断引用变量里的地址是否时同一个地址(同一个对象)
    Master m1=Master.getMaster();
    Master m2=Master.getMaster();
    System.out.println(m1==m2);    

两者比较:

基本都是一样的,都可以获取到一个类的唯一的对象。

1 、在没有使用获取当前类对象之前,懒汉式单例比饿汉式单例在内存上有较少的资源占用。

2 、懒汉式单例在多线程的环境下有问题。需要考虑线程安全

 继承:

是面向对象最显著的一个特征。

继承是从已有的类中派生出新的类,新的类能吸收已有类的数据属性和行为,并能扩展新的能力。

为什么引用继承这个概念:
           可以重复使用父类的中代码,派生出来的子类少写了很多代码,同时还可以进行扩展其他的成员。 提高代码的开发效率。

 已有的类:父类,基类,超类

派生出来的新类:子类,派生类


使用关键字extends来表示子类继承了父类,

语法:

修饰词 class  子类名  extends 父类名{
     //子类的类体
}


class A{}  //父类
class B extends A{}  //B是A的子类
class C extends B{}  //C是B的子类

 继承的特点

  • Java只支持单继承,即:一个类只有一个父类,但是一个类可以有多个子类
  • Java支持多重继承,即:一个类在继承一个父类的同时,还可以被其他类继承,继承具有传递性
  • 父类中的所有成员(成员变量,成员方法,静态变量,静态方法,及其私有的)都被子类继承,但私有的不能被子类访问,没有权限
  • 子类不能继承父类的构造器,只能调用父类的构造器,而且子类中至少有一个构造器一定调用了父类中的某一个构造器(super ())
  • 子类在继承的时候,可以有自己独有的成员变量和成员方法

单继承: 

多重继承:

不同类继承同一个类:

多继承(Java不支持):

 这个创建一个父类:Human

public class Human {String name;private int age;//封装agechar gender;public static int count = 0;//静态属性public Human() {}//无参构造public Human(String name, int age, char gender) {//全参构造this.name = name;this.age = age;this.gender = gender;}public String getName() {//get方法return name;}public void setName(String name) {//set方法this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String toString(){return name+","+age+","+gender;}public static double getPI(){System.out.println(count);return Math.PI;}private double sum(){//私有方法return 1+2;}
}

 继承中的构造器

一个对象在实例化的时候,需要在堆上开辟空间:

堆中的空间分为两部分:

分别是:从父类继承到的属性    和    子类特有的属性

而实例化父类部分的时候,需要调用父类中的构造方法。

强调:子类在创建对象时,从父类中继承过来的属性,是由调用父类中的构造器开辟内存空间的。


默认调用的是父类中的无参构造器。如果父类中没有无参构造器,那么子类需要显式调用父类中的某一个有参构造器。


在子类的构造方法中,使用super(有参传参)调用父类中存在的构造方法,

而且super(有参传参)必须放在首行首句的位置上。

因此,super(有参传参)和this(有参传参)不能在一个构造器中共存(两个都首行首句会冲突)


这里创建个Employee类继承Human类:

public class Employee extends Human{private String name;//子类新定义的属性可以和父类同名private double salary;//子类新增salary属性public Employee(){//无参构造super("xiaohong",23,'女');//传参调用父类中的构造方法}public Employee(String id, double salary, String name,int age,char gender){//全参构造this.name = id;//id赋给子类namesuper.name = name;//name赋给父类中的namethis.salary = salary;//salary赋给salary//this.name = "aaaa";  //父类的私有的成员不能直接访问//setName(name);   //  因为直接调用没有报错,说明继承过来了,前面隐藏了this.setAge(age);//封装属性需要set方法传值this.gender = gender;}public void work(){//验证的静态变量Human.count++;int count1 = Employee.count;System.out.println(count1);// 结果:1  证明子类调用的count值同父类静态count值一样double pi = Human.getPI();// 调用父类的方法访问PI和count值 count=1double pi1 = Employee.getPI();//调用子类继承的父类方法访问 count=1//this.sum(); // 私有的方法没有访问权限, 但是子类已经继承过来了}public String toString(){return name+","+salary+","+getName()+","+getAge()+","+gender;}public static void main(String[] args) {Employee e  = new Employee("1001",2000.0,"小红",23,'女');System.out.println(e.toString());e.work();}
}

 继承中的方法重写

重写,叫做override。

在子类中,对从父类继承到的方法进行重新的实现。

这个过程中,子类重写该方法,会覆盖掉继承自父类中的实现方法,因此,重写又叫做覆写。

为什么重写呢?

        为父类的方法逻辑不能满足子类的需求了,因此子类需要修改逻辑(重写)


重写的特点:

-- 子类只能重写父类中存在的方法。
-- 重写时,子类中的方法名和参数要与父类保持一致。(区别于重载overload)
-- 返回值类型:必须和父类方法的返回值类型相同,或者是其子类型。
-- 访问权限:子类重写方法的访问权限必须大于等于父类方法的访问权限。


注解@Override

用在重写的方法之前,表示验证这个方法是否是一个重写的方法

如果是,程序没有问题。如果不是,程序会报错。

因为我们在进行方法重写的时候,没有什么提示的,因此,在进行重写之前,最好加上去这个注解。

误区:加了@Override就是重写,没有加@Override就不是重写。 这种说法是错误的! @Override只是进行的一个语法校验,与是不是重写无关。

简述 Override 和 Overload 的区别

Override: 是重写,是子类对父类的方法进行重新实现。

Overload: 是重载,是对同一个类中的同名、不同参数方法的描述。


public class Person extends Object {public void sum(int a, int b) {System.out.println(a + b);}protected String getName(){return "小红";}public Animal getAge(){return null;}private Dog getOther(){return null;}
}
//重写
class Student extends Person{@Overridepublic void sum(int a, int b){System.out.println( a * b);}public void sum(int a,int b,int c){}@Overridepublic String getName(){return "小红";}@Overridepublic Dog getAge(){return null;}//@Override    不是重写。protected Dog getOther(){return null;}
}class Animal{}
class Dog extends Animal{}
class Cat extends Animal{}

 Object类型

是所有引用类型的顶级父类(根类)

Object中提供了常用的共有的方法,比如toString,hashCode(),equals等方法

所有的引用类型,包括自定义的类型,都会默认直接或者间接的继承Object.

 toString()方法:

 用来返回对象的属性信息的。
      默认源码如下:   返回的是对象的类全名@hashCode的16进制
       public String toString() {
          return getClass().getName() + "@" + Integer.toHexString(hashCode());
       }
 
      源码的提供的逻辑并不是我们程序要想要的信息(一般不看变量的地址信息)
      因此:需要重写。
      而且该方法不需要手动调用,当对象的变量直接书写在输出语句中,会默认调用toString().


 @Override
    public String toString(){
        return "["+ tid +" "+name +" "+age + "]";
    }

hashCode()方法:

该方法返回的是一个int类型的值。表示对象在内存堆中的一个算法值。
    在自定义类型时,一般都需要重写,减少哈希冲突
    重写原因:
    1.返回的是int类型,值最多是42亿左右。 每个对象都有一个hash值,如果不是自定义,碰撞概率不可控
    2.尽量让对象的所有成员参与运算
    3.尽量自己控制hash值 减少碰撞概率


@Override
    public int hashCode() {
        int hash = 7;
        hash = hash+tid.hashCode();
        hash = hash+name.hashCode();
        hash = hash+age;
        return hash;
    }

equals(Object obj)方法:

用来比较两个对象的属性是否完全相同

源码的逻辑:是比较两个是不是同一个对象,该意义并不大。

 public boolean equals(Object obj) {
              return (this == obj);
         }

重写规则:
         1.  如果 obj == null , 直接返回false
         2.  如果 obj的类型和this的类型不是同一个,直接返回false
         3.  如果传入obj == this. 直接返回true
         4.  然后再比较两个对象的各个属性。

注意:

==   用来比较两边的变量里存储的是否为同一个地址
equals  一般用于比较两个对象的属性。


 @Override
    public boolean equals(Object obj) {
        if(obj == null){
            return false;
        }
       if(obj.getClass() !=this.getClass()){
           return false;
       }
        if(obj == this){
            return true;
        }
       Teacher t = (Teacher) obj;
       return this.tid.equals(t.getTid()) && this.name.equals(t.getName()) && this.age == t.getAge();
    }

 多态:

多态:从字面上理解,就是多种形态,多种状态的含义, 这里指的是一个对象具有多种形态的特点。

简单理解:就是一个对象可以从一种类型转换为另外一种类型。

有向上转型和向下转型两种形式

向上造型(向上转型):

父类型   变量 =  new  子类型();


  • 父类型的变量引用子类型的对象。 Animal a=new Cat();

  • 向上转型肯定会成功,是一个隐式转换。 (小--->大)

  • 向上转型后的对象,将只能够访问父类中的成员(编译期间,看变量类型)

  • 如果调用的是重写过的方法,那么调用的一定是重写方法(运行期间,看对象,即看this是谁)

  • 应用场景:在定义方法时,形式参数是父类型的变量。这样更加灵活,可以传任意子类型的对象,减少代码开发量

向下转型:

子类型   变量名 = (子类型)父类型变量


  • 父类型变量赋值给子类型的变量,需要强制转换,是一个显式转换。(大---->小)

  • 可能会失败,失败的话,会报类造型异常ClassCastException

  • 为了避免ClassCastException ,可以使用instanceof 来判断:变量指向的对象是否属于某一个类型。

  • 为什么向下转型:

                后续代码可能要用到对象的独有行为

// 1、实例化一个Dog对象,并且向上转型
Animal animal = new Dog();

// 2、判断类型,判断animal指向的对象是不是一个Cat类型
if (animal instanceof Cat) {
   System.out.println("animal 的确是一个Cat对象,可以进行向下转型 "); }
else {
   System.out.println("animal不是一个  Cat对象,不能进行向下转型 "); }

 


http://www.mrgr.cn/news/90303.html

相关文章:

  • 计算机毕业设计Spark+大模型知网文献论文推荐系统 知识图谱 知网爬虫 知网数据分析 知网大数据 知网可视化 预测系统 大数据毕业设计 机器学习
  • 全面理解-c++11中的智能指针
  • TLSSled:轻量级 TLS/SSL 安全评估工具!Kali Linux 入门教程!黑客渗透测试!
  • C# COM 组件在.NET 平台上的编程介绍
  • 堆详解及C语言实现
  • 【MQ】Spring3 中 RabbitMQ 的使用与常见场景
  • UE5--浅析委托原理(Delegate)
  • JMeter通过BeanShell创建CSV文件
  • 【CXX-Qt】1.1 Rust中的QObjects
  • 驱动开发系列36 - Linux Graphics 2D 绘制流程
  • 序列化/反序列化与TCP通信协议
  • LLMs之DeepSeek-R1:TinyZero(复现 DeepSeek R1 Zero 的核心功能)的简介、安装和使用方法、案例应用之详细攻略
  • GeekPad智慧屏编程控制
  • 【CXX-Qt】1 CXX-Qt入门
  • Tcp_socket
  • DeepSeek 助力 Vue 开发:打造丝滑的步骤条
  • redo和binlog区别
  • DeepSeek:全栈开发者视角下的AI革命者
  • Docker 部署 MongoDB | 国内阿里镜像
  • 【论文翻译】DeepSeek-V3论文翻译——DeepSeek-V3 Technical Report——第一部分:引言与模型架构
  • Vue07
  • bitcoinjs学习笔记0(预备知识)
  • learn torch 01
  • three.js快速入门学习笔记
  • 渗透利器工具:Burp Suite 联动 XRAY 图形化工具.(主动扫描+被动扫描)
  • 客户端渲染和服务端渲染