欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java SE面向对象--06.类与对象、封装、构造方法

程序员文章站 2022-07-15 18:06:31
...

学习目标:

  • 面向对象
  • 类与对象
  • 三大特征——封装
  • 构造方法

一、面向对象

1.1 引入面向对象(了解)

在开发的过程中,我们经常会遇到在多个程序中使用到同一个功能的情况。如下需求:
需求:定义类,在类中有定义功能对数组求和的需求

class Demo1 {
    public static void main(String[] args) {
        //定义一个数组
        int[] arr={1,2,3,4,5,6,7,8,9};
        //调用自定义函数,求数组的所有数据的和
        int sum = getSum(arr);
        //输出结果
        System.out.println("sum = " + sum);
    }
    /*
        为了解决代码的复用性所以将求和的功能封装到函数中
    */
    public static int getSum( int[] arr ){
        //定义变量,保存和值
        int sum = 0;
        //循环数组,取出数组中的所有数据
        for (int i = 0;i < arr.length;i++ ){
            // 将取出的每个数据累加到sum变量中
            sum += arr[i];
        }
        //将计算的和返回给调用者
        return sum;
    }
}

需求二:在另一个类中也需要使用到数组求和的功能 。

分析:基于前面的学习,我们的解决方案很简单,就是在另外一个类中完成数组求和的代码实现即可。

class Demo2 {
    public static void main(String[] args) {
        //定义一个数组
        int[] arr={1,2,3,4,5,6,7,8,9};
        //调用自定义函数,求数组的所有数据的和
        int sum = getSum(arr);
        //输出结果
        System.out.println("sum = " + sum);
    }
    /*
        为了解决代码的复用性所以将求和的功能封装到函数中
    */
    public static int getSum( int[] arr ){
        //定义变量,保存和值
        int sum = 0;
        //循环数组,取出数组中的所有数据
        for (int i = 0;i < arr.length;i++ ){
            // 将取出的每个数据累加到sum变量中
            sum += arr[i];
        }
        //将计算的和返回给调用者
        return sum;
    }
}

总结:我们观察会发现对数组进行求和值的代码在所有的类中重复。表现为代码的复用性不高。解决上述代码出现的问题,即能否将相同的代码功能抽离出来,单独封装,当需要使用该功能的时候调用即可。

Java SE面向对象--06.类与对象、封装、构造方法

工具类ArrayTools中的代码:

/*
    创建类,用来封装数组求和的功能。
*/
class ArrayTools {
    public int getSum(int[] arr){
        int su m= 0;
        for (int i=0;i<arr.length;i++ ) {
            sum += arr[i];
        }
        return sum;
    }
}

当其他类中需要使用到数组求和功能的代码体现:

/*
    需求:求数组中所有数据的和
*/
class Demo2{
    public static void main(String[] args) {
        //定义一个数组
        int[] arr = {2,4,1,8};
        //然后通过new关键字创建出数组求和所在类的对象
        ArrayTools  util = new ArrayTools();
        //通过对象util就可以调用ArrayUiltl类中的方法了
        int sum = util.getSum(arr);
        //将所求的结果打印到屏幕上
        System.out.println("sum="+sum);
    }
}

1.2 面向对象概述

概述

这里的对象泛指现实中一切事物,每种事物都具备自己的属性行为。面向对象思想就是在计算机程序设计过程中,参照现实中事物,将事物的属性特征、行为特征抽象出来,描述成计算机事件的设计思想。它区别于面向过程思想,强调的是通过调用对象的行为来实现功能,而不是自己一步一步的去操作实现。

区别:

  • 面向过程:强调步骤。

  • 面向对象:强调对象或者结果,这里的对象就是煎饼摊。

1.2 类和对象

  • :是一组相关属性行为的集合。可以看成是一类事物的模板,使用事物的属性特征和行为特征来描述该类事物。

    举例:人就是一个类,属性,就是人的特点:身高、体重、姓名、年龄和性别等。

    ​ 行为:如吃饭、睡觉、学习等。

  • 属性:就是该事物的状态信息。就是特点。

  • 行为:就是该事物能够做什么,有什么动态的行为。就是我们之前学习的方法。

  • 对象:是一类事物的具体体现。对象是类的一个实例,必然具备该类事物的属性和行为。比如狗这类事物中的某一只具体的猫: “旺财” 就是猫这类事物的一个具体对象。

类与对象的关系

  • 类是对一类事物的描述,是抽象的
  • 对象是一类事物的实例,是具体的
  • 类是对象的模板(抽象、类型),对象是类的实体

Java SE面向对象--06.类与对象、封装、构造方法

Java SE面向对象--06.类与对象、封装、构造方法

1.3 类的定义

类的定义格式

public class ClassName { // 类就是用来封装事物属性和行为
  //成员变量
  //成员方法
}
  • 定义类:就是定义类的成员,包括成员变量成员方法
  • 成员变量:和以前定义变量几乎是一样的。只不过位置发生了改变。在类中,方法外
  • 成员方法:和以前定义方法几乎是一样的。只不过把static去掉,static的作用在面向对象后面课程中再详细讲解。
//当前这个class类仅仅只是为了描述一个事物,不需要独立运行所以可以不用写main函数
public class Person {
    // 定义变量描述人的基本特性:成员变量
    double height;//身高
    double weight;//体重
    int age;//年龄
    char sex;//性别
    String name;//姓名

    //定义功能描述人类的行为:成员方法
    public void sleep(){ //睡觉的行为
        System.out.println("sleep");
    }

    public void eat(){ //吃饭的行为
        System.out.println("eat");
    }

    public void study(){ //学习的行为
        System.out.println("study");
    }
}

1.4 对象的使用

对象的使用格式

创建对象:

类名 对象名 = new 类名();
Person p = new Person();

使用对象访问类中的成员:

对象名.成员变量;
p.name
对象名.成员方法名(实际参数);
p.eat();

对象的使用格式举例:

/*
    书写一个测试类在该类中需要使用上面类中的属性和方法
*/
public class PersonTest{
    //需要独立运行,所以需要主方法
    public static void main(String[] args){
        //创建类Person的对象
        Person p = new Person();
        //给类中的属性赋值
        p.age = 18; // 给对象年龄赋值
        p.name = "班导";// 给对象姓名赋值
        p.sex = '女';// 给对象性别赋值

        //使用对象调用Person类中的方法
        person.sleep();
    }
}

1.5 对象的内存图解和练习

需求:描述生活中的车这一类事物

车的基本数据(属性):颜色、品牌(车名)、车牌号码

车的基本行为(功能):跑(运行)

分析和步骤:

​ 定义一个车类class,名字为Car。因为在类Car中不需要独立运行,只是描述车的属性,所以不用定义main方法。

定义完类Car,需要定义一个测试类CarTest来测试车的属性和和功能。

class Car {
    //车的基本属性
    String color;
    String name;
    String number;

    //车的行为
    public void run(){
        System.out.println("车的颜色:"+ color +",车的品牌:" + name);
    }
}
public class CarTest{
    public static void main(String[] args) {
        //创建车的对象
        Car c = new Car();
        //给车的属性赋值
        c.color = "yellow";
        c.name = "BMW";
        c.number= "沪A88888";
        c.run();
    }
}

以上的代码在内存中执行的图解如下图所示:

Java SE面向对象--06.类与对象、封装、构造方法

在使用Java中的new 关键字创建对象时, 这个对象所属类中的所有成员变量,会随着对象在堆中的出现而出现。并且所有的成员变量都会有默认的初始化值。

注意:只要在程序中使用new关键字创建对象,那么在堆中都会有一个新的对象产生。

对象的创建过程:

​ 1、当JVM遇到new关键字时,首先会在堆内存中开辟一个空间,并分配内存地址。

​ 2、类中的所有成员变量会随着对象的出现在堆中出现。并且有自己的默认初始化值。

​ 3、当所有的成员变量加载完成之后,对象创建成功。会将内存地址赋值给左侧变量空间。

成员变量的默认值

数据类型 默认值
基本类型 整数(byte,short,int,long)
浮点数(float,double)
字符(char)
布尔(boolean)
引用类型 数组,类,接口

1.6 类与对象的练习

描述Car类的代码:

 class Car {    
   //车的基本属性    
   String color;    
   String name;    
   String number;    
   //车的行为    
   public void run(){   
     System.out.println("车的颜色:"+ color +",车的品牌:" + name);    
   }
 }

测试类代码:

class CarTest{
    public static void main(String[] args) {
        //创建车的对象
        Car c = new Car();
        //给车的属性赋值 给c对象空间的属性赋值
        c.color = "yellow";
        c.name = "BMW";
        c.number= "沪A88888";
        c.run();
        //创建车的对象 
        Car c1 = new Car();
        //使用c1对象调用run方法 输出数据都为null
        c1.run();
    }
}

说明:因为重新创建了一个新的车的对象,这时在堆中又会有一个新的空间。

由于创建的新的车对象没有进行手动的赋值,这时这个对象中的所有成员变量的值都是默认的值。

出现以上的情况的内存图解说明:

Java SE面向对象--06.类与对象、封装、构造方法

1.6.1对象参数传递

Java SE面向对象--06.类与对象、封装、构造方法

引用类型作为参数,传递的是地址值。

1.7 成员变量和局部变量区别

变量根据定义位置的不同,我们给变量起了不同的名字。如下代码所示:

public class Demo{
    // 成员变量
    int x;
    public static void show(){
        //局部变量
        int y = 10;
    }
}

局部变量:定义在方法中的那些变量。局部变量只能在定义它的方法中有效。

成员变量:定义在类的成员位置上的变量。成员变量在整个类中都有效。(全局变量是成员变量的俗称)。

1) 数据类型 变量名 ;

2) 数据类型 变量名 = 值;

注意:在类和该类的方法中,同时存在一个相同类型相同名称的变量,在方法被执行时,方法中优先使用定义在方法中的变量(局部变量)。

优先级:先使用内部的,再使用外部的(先去内部找,有就使用。没有则再去外部找)就近原则。

局部变量和成员变量的区别:(重要)

1、从定义位置上来讲:

​ 局部变量定义在方法中。

​ 成员变量定义在类中。

2、从内存存储位置上来讲:

​ 局部变量随着方法的运行会在栈内存中出现,局部变量存储在栈内存中。

​ 成员变量会随着对象的出现在堆中存在,成员变量存储在堆内存中。

3、从初始化方式来讲:

​ 局部变量在定义时需要指定初始值(局部变量没有默认值),只有初始化之后才能使用。

​ 成员变量可以不用初始化,有默认值。

4、从存活时间上来讲(生命周期)

​ 局部变量是随着方法的进栈在方法所属的栈内存中存在,随着方法的出栈就消失。

​ 成员变量是随着对象的出现在堆中出现。随着对象的消失而消失。

二、 封装

2.1 封装概述

面向对象 的三个最基本的特征:封装 继承 多态

概述

对事物进行包装。封装会造成事物的部分细节被隐藏。

封装好处

1) 封装可以隐藏事物的细节(提高了安全性)。它把不需要外界直接操作的细节隐藏起来,然后通过第三方的按钮或者接口等手段保证外界还可以间接的去使用这些被隐藏起来的组件。

2) 封装可以提高程序中代码的复用性。(比如方法和类)

面向对象编程语言中的成员变量都是隐藏在对象内部的,外界无法直接操作和修改。封装可以被认为是一个保护屏障,防止该类的代码和数据被其他类随意访问。要访问该类的数据,必须通过指定的方式。适当的封装可以让代码更容易理解与维护,也加强了代码的安全性。

访问封装属性方式

属性隐藏起来,若需要访问某个属性,提供公共方法对其访问。

2.2 封装的步骤

  1. 使用 private 关键字来修饰成员变量。
  2. 对需要访问的成员变量,提供对应的一对 getXxx方法 、setXxx 方法。

2.3 封装的操作——private关键字

需求:描述人这类事物。

/*
    描述人的类
*/
class Person{
    String name;//姓名
    int age;//年龄
    public void say(){
        System.out.println("姓名:"+name+",年龄:"+age);
    }
}
/*
    测试人的类
*/
public class  PersonTest{
    public static void main(String[] args) {
        //创建person对象
        Person person = new Person();
        //通过person对象给属性赋值
        person.name = "黑旋风";
        person.age = -18;
        //使用person对象调用Person类中的功能
        person.say();
    }
}

上述的程序在运行的过程中,给年龄赋了人类非正常的年龄值,但是程序依然可以正常运行。

问题:怎么样控制Person类中的age保证外界在访问的时候,数据是合法的?

要保证在另外一个类中可以控制其他类中的变量时,那么就需要对这个类中的成员变量使用相对应的成员权限修饰符完成。可以使用private 来修饰。从而让外界无法直接访问。

private的含义

  1. private是java中的关键字。一个权限修饰符,代表最小权限。

  2. public和private关键字都属于java中的权限修饰符,可以修饰成员(成员变量、成员方法)。

    private:私有的 访问范围只能在本类中使用 (在java中属于最小的访问权限)

    public:公共的、公开的 访问范围没有底限 (在java中属于最大的访问权限)

  3. 被private修饰后的成员变量和成员方法,只在本类中才能访问。

private的使用格式

private 数据类型 变量名 ;
private 返回值类型 方法名(){}

解决问题方案

我们希望对age这个变量进行控制,于是在Person类中给age 添加了权限修饰符 private ,然后类内提供getXxx和setXx来处理。

/*
    描述人的类
*/
class Person{
    String name;//姓名
    private int age;//年龄
    //对外提供一个给age赋值的函数
    public void setAge( int a ){
        //在给age赋值之前,我们可以通过放方法对传递过来的参数进行判断
        if(a > 0 && a <= 130){
            age = a;
        }else{
            System.out.println("没有这个年龄");
             //throw new RuntimeException("没有这个年龄");
        }
    }
    public void say(){
        System.out.println("姓名:"+name+",年龄:"+age);
    }
}
/*
    测试人的类
*/
class  PersonTest{
    public static void main(String[] args) {
        //创建person对象
        Person person = new Person();
        //通过person对象给属性赋值
        person.name = "黑旋风";
        person.setAge(-18);
        //person.age=-18;
        //使用person对象调用Person类中的功能
        person.say();
    }
}

要求:在定义类的时候,要求类中的所有成员变量全部私有,并对外提供相应公开的访问方法**

一个类中成员变量全部私有,对外提供getXxxx方法或者setXxxx方法 , Xxxx表示的成员变量的名字,而成员变量的名字中的第一个字母要大写。**

演示代码:

/*
    描述人的类
*/
class Person {
    private String name;//姓名
    private int age;//年龄
    //对外提供一个给age赋值的函数
    public void setAge(int a){
        //在给age赋值之前,我们可以通过函数传递过来的参数进行判断
        if(a > 0 && a <= 130){
            age = a;
        }else{
            System.out.println("没有这个年龄");
            //throw new RuntimeException("没有这个年龄");
        }
    }
    public int getAge(){
        return age;
    }
    public void setName(String nm){
        name = nm;
    }
    public String getName(){
        return name;
    }
    public void say(){
        System.out.println("姓名:" + name + ",年龄:" + age);
    }
}
/*
    测试人的类
*/
class  PersonTest{
    public static void main(String[] args) {
        //创建person对象
        Person person = new Person();
        //同过person对象给属性赋值
        //person.name = "黑旋风";
        person.setAge(18);
        int age = person.getAge();
        //person.age=-18;
        //使用person对象调用Person类中的功能
        person.say();
        System.out.println(age);
    }
}

总结:

在以后写代码的时候,要求类中的所有成员变量全部使用private关键字修饰私有,然后提供公开的getXxxx() 和 setXxxx()方法。

2.4 封装之——this关键字

我们发现 setXxx 方法中的形参名字并不符合见名知意的规定,那么如果修改与成员变量名一致,程序的可读性是否更好一些呢?代码如下:

/*
    描述人的类
*/
class Person{
    private String name;//姓名
    private int age;//年龄
    //对外提供一个给age赋值的函数
    public void setAge(int age){
        //在给age赋值之前,我们可以通过函数传递过来的参数进行判断
        if(age > 0 && age <= 130){
            age = age;
        }else{
            System.out.println("没有这个年龄");
            //throw new RuntimeException("没有这个年龄");
        }
    }
    public int getAge(){
        return age;
    }
    public void setName(String name){
        name = name;
    }
    public String getName(){
        return name;
    }
    public void say(){
        System.out.println("姓名:" + name + ",年龄:" + age);
    }
}

经过修改和测试,我们发现新的问题,成员变量赋值失败了。也就是说,在修改了setXxx() 的形参变量名后,方法并没有给成员变量赋值!这是由于形参变量名与成员变量名重名,基于就近原则,无法访问到成员变量,从而赋值失败。想要解决这个问题,我们可以使用使用this关键字来完成。

this的含义

this代表所在类的当前对象的引用(地址值),即对象自己的引用。

记住 :方法被哪个对象调用,方法中的this就代表那个对象。即谁在调用,this就代表谁。

this使用格式

this.成员变量名;

当成员变量和局部变量重名时,可以使用 this 进行区分,代码如下:

/*
    描述人的类
*/
class Person{
    private String name;//姓名
    private int age;//年龄
    //对外提供一个给age赋值的函数
    public void setAge(int age){
        //System.out.println(this);
        //在给age赋值之前,我们可以通过函数传递过来的参数进行判断
        if(age > 0 && age <= 130){
            this.age = age;
        }else{
            System.out.println("没有这个年龄");
            //throw new RuntimeException("没有这个年龄");
        }
    }
    public int getAge(){
        return this.age;
    }
    public void setName(String name){
        this.name = name;
    }
    public String getName(){
        return this.name;
    }
    public void say(){
        System.out.println("姓名:"+this.name+",年龄:"+this.age);
    }
} 

小贴士:方法中只有一个变量名时,默认也是使用 this 修饰,可以省略不写。

注意:在方法中,如果局部变量和成员变量名字和类型都一致,那么方法会先使用局部变量。

总结:

this的应用:

1、this是用来记录当前调用方法的对象的地址。

2、this可以区分成员变量和局部变量。格式:this.成员变量名 ;

三、构造方法

当我们在创建一个对象的时候,如果需要对象创建好之后,就拥有一些属于自己本身的特定数据,而不是后面通过程序去修改。那么也就是说在创建对象的过程中,就应该把这些数据赋值完成。

需求:描述学生这类事物

/*
    创建一个学生的类
*/
class Student{
    //学生的姓名
     String name;
    //学生的年龄
     int age;
    //学生的住址
    String address;
    // 成员方法
    public void speak(){
        System.out.println("姓名:"+name+"年龄:"+age+"住址:"+address);
    }
}
public class  StudentTest{
    public static void main(String[] args) {
        //创建学生的对象
        Student s = new Student();
        //使用学生的对象student调用Student类中的方法
        s.speak();
    }
}

在StudentTest类中,创建完的Student对象所有的属性都是默认的值, 而在现实生活中,每个学生入校之前 ,都会有自己的年龄、姓名、地址。那么我们更希望在对象创建完之后就让每个学生具备属于自己的这些数据,而不是通过对象创建完之后,再通过其他的代码进行修改。

要解决这个问题,就必须使用Java中提供的构造方法完成。

3.1 构造方法介绍

什么是构造方法?

构造方法:构造方法是用来初始化该对象,给对象的成员变量赋初始值,从而完成对象的创建。

在我们的程序中如果使用new 关键字,这时就是在创建对象,而我们在创建完对象的过程中,会调用当前这个类的构造方法来完成对象的创建。那么既然在new对象的时候,会调用对应的构造方法,这时我们就可以在new对象的过程中,给构造方法传递参数,完成对象信息的初始化动作。

一般方法的定义格式:

修饰符  返回值类型  方法名(参数列表....){
    方法体;
}

构造方法的定义格式:

修饰符  构造方法名( 参数列表.. ){
    方法体;
}

构造方法定义的细节:

​ 1、构造方法是用来创建对象的,它不需要书写返回值类型。

​ 2、构造方法的名字要求必须和当前的类名一致。因此构造方法的名称书写和类名一致。

​ 3、参数列表,可以有参构造和无参构造

学生类使用构造方法完成对象的初始化创建:

// 创建学生类
class Student{
    //学生的姓名
     String name;
    //学生的年龄
     int age;
    //学生的住址
    String address;
    //创建构造方法
    public Student(String name,int age,String address){//接收创建对象时传递的参数
        this.name = name;
        this.age = age;
        this.address = address;
    }
    // 成员方法
    public void speak(){
        System.out.println("姓名:"+name+"年龄:"+age+"住址:"+address);
    }
}
public class  StudentTest{
    public static void main(String[] args) {
        //创建学生的对象
        Student s = new Student("黑旋风",20,"上海");//创建对象时传递具体的数据
        //调用方法查看输出数据
        s.speak();
    }
}

构造方法的作用:

​ 在创建对象的过程中,根据new对象时传递的数据,对当前创建出来的对象中的成员变量进行指定数据的初始化。没有构造方法就无法完成对象的创建。

3.2 默认构造方法

默认构造方法: 没有学习构造方法之前,对象就可以正常创建。也就是说,在对象所属的类中,本身存在构造方法,否则是不能创建对象的。只是这个构造方法是隐式存在的,我们看不见当。java编译器编译完某个java源代码之后,生成的class文件中,会自动生成一个没有参数的构造方法即:默认构造方法。

因为类定义完成之后,类中就已经存在一个默认的构造方法了,这也是我们可以在没有接触构造方法之前依然能正常创建对象的原因。

class Teacher{
    //属性
    String name;
    int age;
    public void say(){
        System.out.println("姓名:"  name+",年龄:" + age);
    }
}
public class TeacherTest{
    public static void main(String[] args) {
        Teacher t = new Teacher();
        System.out.println("Hello World!");
    }
}

上述的Teacher类编译完之后,class文件中就会生成一个 public Teacher(){} 构造方法。在new对象的时候,没有传递任何数据,就会调用这个构造函数。

注意:定义类的时候,如果类中定义了有参数的构造方法,系统将不再提供默认的构造方法。所以一般在定义类的时候,如果手动定义了有参的构造方法,根据需求的不同,有时也需要定义无参的构造方法。这样就会导致一个类中有多个构造方法以重载的方式存在。

代码演示:

// 描述人这类事物
class Person{
    // 成员变量
    String  name; // 姓名
    int age; //年龄
    char sex; // 性别

    // 无参构造方法
    public Person(){}

    // 给姓名 年龄 初始化的构造方法
    public Person( String name, int age,char sex ){
        this.name = name;
        this.age = age;
        this.sex = sex;
    }
}
// 测试构造方法
public class PersonTest {
    public static void main(String[] args) {
        // 使用无参构造 创建对象
        Person p1 = new Person();

        // 使用有参构造 创建对象
        Person p2 = new Person("张三丰",120,'男');
    }
}

提问:

​ 在一个类中,方法可以以重载的方式出现,并且重载的方法之间也可以相互调用。那么构造方法之间可以相互调用吗? 如果在一个类中出现了一个普通的方法和构造方法名一致,会出现什么情况?

代码演示:

class ConsDemo{
    // 成员变量
    int a;
    int b;
    int c;
    // 给a b两个成员变量赋值的构造方法
    public ConsDemo(int a, int b) {
        this.a = a;
        this.b = b;
    }
    /*
        给 abc3个成员变量赋值的方法,因为给ab变量赋值的构造方法已经存在,
        那么在给abc3个变量赋值的构造方法中能够直接调用给ab赋值的构造呢?
        这样就可以不用在给3个变量的构造方法中多写一次赋值的代码,提高代码
        的复用性。
    */
    public ConsDemo(int a, int b, int c) {
        /*
            这里在使用方法名直接调用的时候发现调用的不是给ab赋值的构造方法
            而是方法名和类名一致的普通方法。
        */
        // ConsDemo(a,b);

        /*
            想要完成构造方法之间的相互调用,可以使用this关键字完成,语法格式:
                this(参数列表);
                this语句调用构造方法,需要放在构造方法的第一行位置
                具体调用的是哪一个构造方法,取决于传递的参数,会调用和传递参数
                一致的构造方法
        */
        this(a,b);
        this.c = c;
        System.out.println("3个参数的构造方法运行....");
    }
    // 空参的构造方法
    public ConsDemo() {}
    // 方法名和类名一致的普通方法,仅为演示,开发中禁止这样给普通方法命名。
    public void ConsDemo( int x , int y ){
        System.out.println( "Demo= " + (x + y) );
    }
}
public class StaticCodeDemo {
    public static void main(String[] args) {
       Demo d = new Demo(1,2,3);
       System.out.println(d.a + "..." + d.b + "..." + d.c);
    }
}

this作用总结

this表示的一个变量,指向的是一个引用地址,它有3个作用。

​ 1、this可以区分成员变量和局部变量。

​ 2、this可以记录当前方法是被哪个对象调用的,哪个对象调用的方法this就指向哪个对象

​ 3、this可以完成构造方法之间的调用:this(参数列表); 调用和传递参数一致的构造方法

​ this在调用构造方法的时候需要注意:

​ a) this语句必须放在第一行

​ b) 两个构造方法之间不能相互调用,否则会导致内存的溢出。编译时直接报错。

3.3 构造方法和一般方法异同

1、它们的执行时间不同:

​ 构造方法是在创建对象的过程中执行。当对象创建完成了,构造方法就已经执行结束。

​ 一般方法是创建完对象之后,通过对象直接调用。如果在同一个类中,可以直接调用。

2、它们的调用次数不同:

​ 构造方法只有在new对象的时候会被调用,对象创建完成,不能手动的人为去调用构造方法。

​ 一般方法可以通过对象随意的调用,没有次数限制。

3、定义方式不同:

​ 一般方法: 修饰符 返回值类型 方法名( 参数列表……){ 方法体 }

​ 构造方法: 修饰符 类名( 参数列表…… ){ 方法体 }

3.4 注意事项

  1. 如果不定义造方法,编译器会自动生成无参数构造方法。

  2. 如果定义了构造方法,编译器将不再生成无参数构造方法。

  3. 构造方法是可以重载的,既可以定义参数,也可以不定义参数。

3.5 构造方法的执行和内存图解

在创建对象的过程中构造方法的执行时机

​ 1、通过new 关键字创建对象,首先会在堆中分配对象的内存空间

​ 2、给对象所属类中的所有成员变量进行默认的初始化赋值。

​ 3、调用和new对象时传递参数一致的构造方法。

​ 4、开始执行构造方法中的所有代码。

​ (构造方法中有隐式的3步:super()、成员变量显示赋值、执行构造代码块。后面课程会讲到)

​ 5、构造方法中的所有代码执行结束,构造方法出栈,此时才表示当前这个对象创建完成。

​ 6、把对象的内存地址赋值给对应的引用变量。

Java SE面向对象--06.类与对象、封装、构造方法

3.6 构造代码块

代码块是指被{}封装起来的代码。

构造代码块:定义在类的成员位置,直接使用{ },在{}中写代码内容。

位置:类中,方法外,和方法并列,和先后位置无关。

执行时机:创建对象的时候执行一次。在构造方法隐式3步的最后一步。当构造代码块执行结束之后,开始执行构造方法本身的代码内容。

格式:

public class ClassName{
  // 构造代码块
  {
    // 执行语句 
  }
}

作用:所有构造方法中都需要执行的代码,书写在构造代码块中。

代码演示:

/*
    演示构造代码块的执行
    需求: 在每一个构造方法中都需要执行一句: HelloWorld!
 */
class ConsCode{
    // 成员变量
    int a;
    int b;

    // 空参构造
    public ConsCode() {
        //System.out.println("HelloWorld!");
        System.out.println("我在构造代码块执行结束之后执行...");
    }

    // 给变量a赋值的构造
    public ConsCode(int a) {
        this.a = a;
        System.out.println("我在构造代码块执行结束之后执行...");
        //System.out.println("HelloWorld!");
    }

    // 给变量ab同时赋值的构造
    public ConsCode(int a , int b) {
        this.a = a;
        this.b = b;
        //System.out.println("HelloWorld!");
        System.out.println("我在构造代码块执行结束之后执行...");
    }

    // 构造代码块
    {
        System.out.println("HelloWorld!");
    }
}
public class ConstructorCodeDemo {
    public static void main(String[] args) {
        // 分别使用3个构造方法创建对象,并运行程序,显示执行3遍HelloWorld!
        ConsCode c1 = new ConsCode();
        ConsCode c2 = new ConsCode(10);
        ConsCode c3 = new ConsCode(10,20);
    }
}

四、 标准代码——JavaBean

什么是JavaBean

JavaBean是 Java语言编写类的一种标准规范。JavaBean在任何官方网站都没有一个正式的概念来定义它,只是我们根据它的特点来定义它为JavaBean,其实他就是一个普通的Java类,用来封装数据的,它具备如下特点:

1、要求这个类必须拥有一个公共的空参数的构造方法,外界可以访问到;

2、它如果有属性(成员变量),全部必须私有;

3、对外提供公共的get或者set方法,并且方法的命名要遵守一定的规则。

举例:

属性 name —– 方法 getName setName (属性首字母大写,在前面加上 get和set )。

属性 age —– 方法 getAge setAge 。

把这样的一个类称为一个JavaBean类。这个类在程序中主要用于封装数据。

new一个对象,通过set方法给所有的属性设置值,在其他的程序就可以通过这个对象的get方法获取值。

如下所示:

public class ClassName{
    //成员变量
    //构造方法
    //无参构造方法【必须】
    //有参构造方法【建议】
    //成员方法  
    //getXxx()
    //setXxx()
}

编写符合JavaBean 规范的类,以学生类为例,标准代码如下:

public class Student {
    //成员变量
    private String name;
    private int age;

    //构造方法
    public Student() {}

    public Student(String name,int age) {
        this.name = name;
        this.age = age;
    }

    //成员方法
    publicvoid setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    publicvoid setAge(int age) {
        this.age = age;
    }

    publicint getAge() {
        return age;
    }
}

测试类,代码如下:

public class TestStudent {
    public static void main(String[] args) {
        //无参构造使用
        Student s= new Student();
        s.setName("柳岩");
        s.setAge(18);
        System.out.println(s.getName()+"---"+s.getAge());

        //带参构造使用
        Student s2= new Student("赵丽颖",18);
        System.out.println(s2.getName()+"---"+s2.getAge());
    }
}