[Javase]面向对象(一)——封装、构造函数、this关键字、static关键字、单例设计模式

271 阅读13分钟

面向对象的概念

  • 面向对象是相对面向过程而言
  • 面向对象和面向过程都是一种思想
  • 面向过程: 强调的是功能行为
  • 面向对象: 将功能封装进对象,调用具备了功能的对象。
  • 面向对象是基于面向过程的 在以后的开发中其实就是找对象使用。没有对象,就创建对象。

类和对象的关系

  • 现实生活中的对象:张三、李四

  • 想要描述:提取对象中共性内容。对具体的抽象

  • 描述时:这些对象的共性有:姓名,年龄,性别,学习的功能。

  • 而描述对象时在java中就是用的形式来体现的。而对象是java中通过new操作产生的一个实体,在推内存中存在。

    类就是:对现实生活中事物的描述
    对象:就是这类事物,实实在在存在的个体。

    通过图来解释就如下图:

可以理解为:类就是图纸而汽车就是堆内存中的对象。 练习:描述一个汽车类,里面有颜色和品牌属性,并且有一个run方法

class Car{
    public String color;
    public String pinpai;

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

属性对应的是类中的变量,行为对应的是类中方法。属性和行为共同称为类中的成员(成员变量,成员方法)

生产汽车:在java中通过new操作符来完成。其实就是在堆内存上产生一个实体。

//此时c就是一个类类型变量
Car car=new Car();

类类型的变量car指向对象在堆内存中的地址

现有一个需求:将已有车car的颜色改为blue。指挥该对象做使用。在java中指挥方式是:对象.对象成员

car.color="bule";
car.run();

成员变量和局部变量

  • 作用范围:成员变量作用于整个类中,局部变量作用于函数或语句中。
  • 在内存中的位置不同:成员变量在堆内存中,因为对象的存在,才在内存中存在;局部变量存在栈内存中。

面向对象的第一大特征:封装

封装:是指隐藏对象的属性实现细节,仅对外提供公共访问方式。

  • 好处:
    • 将变化隔离
    • 便于使用
    • 提高重用性
    • 提高安全性
  • 封装原则:
    • 将不需要对外提供的内容都隐藏起来。
    • 把属性都隐藏,提供公共方法对其访问。(例如set、get方法)

通过关键字private修饰类中的成员(成员变量,成员函数),使其变为私有的,私有的成员只在本类有效。将成员属性私有化后,类以外即使建立了的对象也不能直接访问,需要提供set和get方法对成员变量进行操作。

注意:私有仅仅是封装的一种表现形式。不能说封装就是将成员私有化

构造函数

  • 特点:
    • 函数名与类名相同
    • 不用定义返回值类型
    • 不可以写return语句
  • 作用:
    • 给对象进行初始化。
  • 注意:
    • 默认构造函数的特点:多个构造函数使用重载的形式存在的(方法名相同,参数不同)

观察下面代码: 对象一建立就会调用与之对应的构造函数。构造函数的作用:可以用于给对象进行初始化。

class Car{
    public Car(){
        System.out.println("Car run");
    }
}
public class teat{
    public static void main(String[] args){
        Car car=new Car();
    }
}

构造函数小细节: 当一个类没有定义构造函数时,系统会默认给该类添加一个无参构造函数。若在类中自定义了构造函数后,系统将不在提供默认的构造函数。当对象创建时,就会调用类中的构造方法。

下面的程序,自定义了无参构造方法,单参、两参构造方法,并在主函数中分别初始化。

class Person{
    //定义两个成员变量
    private String name;
    private String age;

    //自定义无参构造方法
    public Person(){
        System.out.println("无参构造方法初始化");
    }
    public Person(String name){
        this.name=name;
        System.out.println("name初始化");
    }
    public Person(String name,String age){
        this.name=name;
        this.age=age;
        System.out.println("name、age初始化");
    }
}

public class teat{
    public static void main(String[] args){
        Person person0=new Person();
        Person person1=new Person("zhangsan");
        Person person2=new Person("lisi","18");
    }
}

构造函数和一般函数在写法上不同。在运行上也有不同:

  • 构造函数是在对象一建立就运行,给对象初始化。而一般方法是对象调用才执行,是给对象添加对象具备的功能。
  • 一个对象建立,构造方法只运行一次,而一般方法可以被该对象调用多次。

构造代码块

给对象进行初始化用的,对象一旦建立就执行,并且优先于构造函数执行。

classPerson{
    //定义两个成员变量
    private String name;
    private String age;
    //构造代码块
    {
        System.out.println("这是一个构造代码块");
    }
}

构造函数和构造代码块的区别:构造代码块是给所有对象进行统一初始化,而构造函数是给对应的对象初始化。用于定义对共性的初始化内容。

this关键字

this代表他所在函数所属对象的引用。简单地说哪个对象在调用this所在的函数,this就代表哪个对象。

//类中的构造方法
public Person(String name,String age){
    this.name=name;
    this.age=age;
    System.out.println("name、age初始化");
}

//在主函数中new一个对象
Person person2=new Person("lisi","18");

这是表示new了一个名为person2的Person类的对象,并且调用两个参数的构造方法,因为是person2调用的所以此时构造方法中的this代表了person2,构造方法中表示将传入的值赋给person2对象在堆内存中nama属性和age属性。

this关键字的基本引用:当定义类中函数时,该函数内部要用该类对象时,这时用this来表示这个对象。

需求:给Person类定义一个比较年龄的功能,和另一个对象比较年龄。在类中添加方法:

public boolean comAge(Person person){
    return this.age==person.age;
}

主函数中调用:比person1和person2的年龄

Person person1=new Person("zhangsan","19");
Person person2=new Person("lisi","18");
System.out.println(person1.comAge(person2));

this关键字在构造函数间调用(构造函数间调用只能用this语句)

//在两个参数的构造方法中调用,一个参数的构造方法,提高代码的复用性
public Person(String name){
    this.name=name;
    System.out.println("name初始化");
}
public Person(String name,String age){
    this(name);
    this.age=age;
    System.out.println("name、age初始化");
}

注意事项:this语句只能定义在构造函数的第一行,否则编译失败。因为初始化动作必须先执行。

static(静态)关键字

static关键字:用于修饰成员(成员属性和成员方法)

被修饰后具备以下特点:

  • 随着类的加载而加载(随着类的消失而消失。说明他的生命周期最长)
  • 优先于对象存在(静态先存在。对象后存在)
  • 被所有对象所共享
  • 可以直接被类名调用

使用注意:

  • 静态方法只能访问静态成员 (非静态方法既可以访问静态也可以访问非静态)
  • 静态方法中不可以写this、super关键字 (因为静态优先于对象存在,所以静态方法中不可以出现this,super关键字 )
  • 主函数是静态的

实例变量和类变量(静态变量)的区别:

  1. 存放位置
  • 类变量(静态变量)随着类的加载而存在于方法区中。
  • 实例变量随着对象的建立而存在堆内存中。
  1. 生命周期
  • 类变量(静态变量)生命周期最长,随着类的消失而消失
  • 实例变量生命周期随着对象的消失而消失

静态有利有弊:

  • 利处:对对象的共享数据进行单独空间的保存,无需每一个对象都存储一个,可以直接被类名调用
  • 弊端:生命周期过长。访问出现局限性(只能访问静态,不能访问动态成员)

主函数

是一个特殊的函数。作为程序的入口,可以被jvm调用

public static void main(String[] args)

主函数的定义:

  1. public:代表着该函数的访问权限是最大的
  2. static:代表主函数随着类的加载就已经存在了(静态)
  3. void:主函数没有具体的返回值
  4. main:不是关键字,但是是一个特殊的单词,可以被jvm识别
  5. (String[] args):函数的参数,参数类型是一个数组,该数组中的元素是字符串。字符串类型的数组

主函数也是一个函数,可以看到主函数传入了一个String类型的数组,那究竟jvm调用主函数时传递了什么参数给主函数呢?

//尝试打印里面的内容
public static void main(String[] args){
    System.out.println(args);
    System.out.println(args.length);
}

结果为:

可以发现jvm在调用主函数的时候,传入的是new String[0]

现在在配置中进行如下配置:

在调用主函数的时候传入三个数字,然后再主函数中遍历数组并打印:

能成功打印出数组中的三个数字,并且计算出数组长度。

什么时候使用静态?

  • 要从两方面下手,因为静态修饰的内容有成员变量和成员函数。

什么时候定义静态变量呢?

  • 当对象中出现共享数据时,即所有对象中都一样,则该数据被静态所修饰,存在方法区中。
  • 若是对象中的特有数据,则要定义成非静态存在于堆内存中。

什么时候定义静态函数呢?

  • 当功能内部没有访问到非静态数据(对象特有数据),那么该功能可以定义成静态的。

工具类与帮助文档的制作

先自定义一个工具类,首先将无参构造方法私有化,让外部只能通过类名调用类中的静态方法。并且详细编写每个方法的文档注释。

public class util{
    //将无参构造方法设为私有,让外部无法实例化对象,仅能通过类名调用类中的静态方法
    /**
    *空参数构造函数
    */
    private util(){}
    //工具类中定义获取最大值和获取最小值方法
    
    /**
    *获取整形数组中的最大值
    *@paramarr接收一个int类型的数组
    *@return返回数组中的做大值
    */
    public static int getMax(int[]arr){
        int max=0;
        for(int x=1;x<arr.length;x++){
            if(arr[x]>arr[max]){
                max=x;
            }
        }
        return arr[max];
    }
    
    /**
    *获取整形数组中的最小值
    *@paramarr接收一个int类型的数组
    *@return返回数组中的做小值
    */
    public static int getMin(int[] arr){
        int min=0;
        for(int x=1;x<arr.length;x++){
            if(arr[x]>arr[min]){
                min=x;
            }
        }
        return arr[min];
    }
    
    /**
    *给数组进行选择排序
    *@paramarr接收受一个数组
    */
    public void selectSort(int[] arr){
        for(int x=0;x<arr.length-1;x++){
            for(int y=x+1;y<arr.length;y++){
                if(arr[x]>arr[y]){
                    swap(arr,x,y);
                }
            }
        }
    }
    
    /**
    *给数组进行冒泡排序
    *@paramarr接收一个数组
    */
    public static void bubbleSort(int[] arr){
        for(int x=0;x<arr.length-1;x++){
            for(int y=0;y<arr.length-x-1;y++){
                swap(arr,x,y);
            }
        }
    }
    
    /**
    *给数组中的元素进行位置的置换
    *@paramarr接受一个int型的数组
    *@parama需要置换的第一个参数
    *@paramb需要置换的第二个参数
    */
    private static void swap(int[] arr,int a,int b){
        int temp=arr[a];
        arr[a]=arr[b];
        arr[b]=temp;
    }
    
    /**
    *用于打印数组中的元素。打印形式时[a,b,c]
    *@paramarr接收一个数组
    */
    public static void printArray(int[]arr){
        System.out.println("[");
        for(intx=0;x<arr.length;x++){
            if(x!=arr.length-1){
                System.out.println(arr[x]+"、");
            }else{
                System.out.println(arr[x]+"]");
            }
        }
    }
}

接下来,将.class文件发送给其他人,让其他人也可以使用这个工具类,只要将给文件设置到classpath路径下,就可以使用该工具类。但是该类之中到底定义了什么方法,对方并不清楚,接下来开始制作程序的说明书。java的说明书通过文档注释完成! 再命令行窗口使用javadoc命名可以自动生成说明文文档,再idea中可以通过tool下的:

静态代码块

随着类的加载而加载,只执行一次。用于给类进行初始化,并优先于主函数。静态代码块给进行初话,构造代码块给对象进行初始化。构造函数给对应对象进行初始化的。 格式:

static
{
    静态代码块中的执行语句
}

对象初始化过程:

Person person=new Person();
  1. 因为new用到了Person.class所以会先找到Person.class文件并加载到内存中
  2. 在栈内存中开辟空间用于存储引用变量person
  3. 静态代码块被执行
  4. 开辟空间用于存放成员属性,分配内存地址(new对象创建之前,首先会去方法区找是否有对应的class文件,有,然后把class文件的成员变量拿到新创建的对象中。而方法不用拿,调用时,只需取出来用即可。)
  5. 在堆内存中建立对象的特有属性、进行默认初始化(null)
  6. 显示初始化
  7. 构造代码块初始话
  8. 构造方法初始化
  9. 将对象地址交给占内存的person进行保存

单例设计模式

设计模式(思想):解决某一类问题最行之有效的方法。java中共有23种设计模式:

单例设计模式:决一个类在内存中只存在一个对象。

想要保证对象唯一性,应该遵循一下几点:

  1. 为了避免其他程序过多建立该类对象。先禁止其他程序建立该类对象。
  2. 为了让其他程序可以访问到该类对象,只好在本类中,自定义一个对象。
  3. 为了方便其他程序对自定义对象的访问,可以对外提供一些访问方式。

这三部如何用代码体现呢?

  1. 将构造方法私有化。
  2. 在类中创建一个本类对象
  3. 提供一个方法可以获取到该对象

演示:当需要保证对象唯一时,就可以使用单例设计模式。

class Single{
    //构造方法私有化
    private Single(){}
    //自定义对象
    private static Single single=new Single();
    //返回给外部一个对象
    public static Single getInstance(){
        return single;
    }
}

这个是先初始化对象,称为:饿汉式。Single类一进入内存就创建好了对象

单例设计模式的第二种体现:

class Single{
    //构造方法私有化
    private Single(){}
    //自定义对象
    private static Single single=null;
    //返回给外部一个对象
    public static Single getInstance(){
        if(single==null){
            return new Single();
        }else{
            return single;
        }
    }
}

这是方法被调用时才初始化没也成为对象的演示假造,称为:懒汉式。当外部需要时,才进行实例化。