【Java基础】-- 第7章:面向对象编程(初级)

Java基础

七、面向对象编程(初级)

类与对象

一个程序就是一个世界,有很多事物(对象[属性、行为])

image-20221013112012080

image-20221013112118306

public class Demo01 {
    public static void main(String[] args)
    {
        //使用面向对象解决
        //实例化一只猫
        //1.new Cat()创建一只猫
        //2.Cat cat1 = new Cat();把创建的猫赋给cat1
        //cat1就是一个对象
        Cat cat1 = new Cat();
        cat1.name = "小白";
        cat1.age = 3;
        cat1.color = "白色";
        //创建第2只猫
        Cat cat2 = new Cat();

        //访问对象的属性
        System.out.println("第一只猫的信息:"+cat1.name+" "+cat1.age +" "+cat1.color);

    }
}
//使用面向对象
//定义一个猫类
class Cat{
    //属性
    String name;
    int age;
    String color;
}

可以看出:

  • 类是抽象的,概念的,代表一类事物
  • 对象是具体的,实际的,代表一个具体事物
  • 类是对象的模板,对象是类的一个个体,对应一个实例

对象在内存中的布局

image-20221013112140118

属性/成员变量

基本介绍:

1)从概念或叫法上看:成员变量 = 属性 = field(字段),即成员变量是用来表示属性的

2)属性是类的一个组成部分,一般是基本数据类型,也可以是引用类型(对象、数组)

注意事项:

1)属性的定义语法同变量,示例:访问修饰符 属性类型 属性名;

访问修饰符:控制属性的访问范围

有四种访问修饰符:public,proctected,默认,private

2)属性的定义类型可以为任意类型,包含基本类型或引用类型

3)属性如果不赋值,有默认值,规则和数组一致

//编写一个main 方法
public static void main(String[] args) {
    //创建Person 对象
    //p1 是对象名(对象引用)
    //new Person() 创建的对象空间(数据) 才是真正的对象
    Person p1 = new Person();
    //对象的属性默认值,遵守数组规则:
    //int 0,short 0, byte 0, long 0, float 0.0,double 0.0,char \u0000,boolean false,String null
    System.out.println("\n 当前这个人的信息");
    System.out.println("age=" + p1.age + " name="
                       + p1.name + " sal=" + p1.sal + " isPass=" + p1.isPass) ;
}
}
class Person {
    //四个属性
    int age;
    String name;
    double sal;
    boolean isPass;
}
创建对象

1)先声明再创建

Cat cat; //声明对象cat

cat = new Cat(); //创建

2)直接创建

Cat cat = new Cat();

访问属性:
基本语法:

对象名.属性名;

类和对象的内存分配机制

具体分析:

image-20221013121809428

Java内存结构分析

1)栈:一般存放基本数据类型(局部变量)

2)堆:存放对象(Cat cat,数组等)

3)方法区:常量池(常量,比如字符串),类加载信息

对以下代码创建对象的流程简单分析:

Person p = new Person();
p.name = "jack";
p.age = 10;

1)先加载Person类信息(属性和方法信息,只会加载一次)

2)在堆中分配空间,进行默认初始化(看规则)

3)把地址赋给p,p就指向对象

4)进行指定初始化,比如p.name = “jack” p.age = 10

例子:

Person a = new Person();
a.age = 10;
a.name = "小明";
Person b;
b = a;
System.out.println(b.name); //小明
b.age = 200;
b = null;
System.out.println(a.age);  //200
System.out.println(b.age);  //异常

image-20221013122116752

成员方法

基本介绍:

在某些情况下,我们需要定义成员方法(简称方法)比如人类:除了有一些属性外( 年龄,姓名…),我们人类还有一些行为比如:可以说话、跑步…,通过学习,还可以做算术题。这时就要用成员方法才能完成。现在要求对Person 类完善。

代码:

public class Demo02 {
    public static void main(String[] args) {
        //方法使用
        //1.方法写好后,如果不去调用,不会输出
        //2.先创建一个对象,然后调用其方法即可
        Person p1 = new Person();
        p1.speak();  //调用方法
        p1.cal01(5); //调用cal01方法,同时给n = 5
        int ans = p1.getSum(10,20); //调用getSum方法
        System.out.println("getSum返回的值是:"+ans);
    }
}
class Person{
    String name;
    int age;
    //方法(成员方法)
    //添加speak成员方法,输出"我是一个好人"
    //1.public表示方法是公开
    //2.void:表示方法没有返回值
    //3.speak():speak是方法名,()形参列表
    //4.{}方法体,可以写我们要执行的代码
    //5.System.out.println("我是一个好人"),表示我们的方法就是输出一句话
    public void speak()
    {
        System.out.println("我是一个好人");
    }
    //1.(int n)形参列表,表示当前有一个形参,可以接收用户的输入
    public void cal01(int n)
    {
        int res = 0;
        for(int i = 1; i <= n; i++) {
            res += i;
        }
        System.out.println("cal01 方法计算结果=" + res);
    }
    //1.public表示方法是公开的
    //2.int:表示方法执行后,返回一个int值
    //3.getSum:方法名
    //4.(int num1,int num2)形参列表,2个形参,可以接收用户传入的两个数
    //5.return res:表示把res的值返回
    public int getSum(int num1,int num2)
    {
        int res = num1 + num2;
        return res;
    }
}

成员方法的好处:

  • 提高代码的复用性
  • 可以将实现的细节封装起来,然后供其他用户来调用即可
方法调用机制原理

image-20221013112416694

成员方法的定义
访问修饰符 返回数据类型 方法名(形参列表...){
    //方法体语句;
    return 返回值;
}

1)形参列表:表示成员方法输入

2)返回数据类型:表示成员方法输出,void表示没有返回值

3)方法主体:表示为了实现某一功能代码块

4)return语句不是必须的

注意事项和细节:

1)访问修饰符

  • 作用是控制方法使用的范围
  • 如果不写默认访问(有四种:public,protected,默认,private)

2)返回数据类型

  • 一个方法最多有一个返回值
  • 返回类型可以为任意类型,包含基本类型或引用类型(数组,对象)
  • 如果方法要求有返回数据类型,则方法体中最后的执行语句必须为return值;而且要求返回值类型必须和return的值类型一致或兼容
  • 如果方法是void,则方法体中可以没有return 语句,或者只写return
public class MethodDetail {
    public static void main(String[] args) {
        AA a = new AA();
        int[] res = a.getSumAndSub(1, 4);
        System.out.println("和=" + res[0]);
        System.out.println("差=" + res[1]);
        //细节: 调用带参数的方法时,一定对应着参数列表传入相同类型或兼容类型的参数
        byte b1 = 1;
        byte b2 = 2;
        a.getSumAndSub(b1, b2);//byte -> int
        //a.getSumAndSub(1.1, 1.8);//double ->int(×)
        //细节: 实参和形参的类型要一致或兼容、个数、顺序必须一致
        //a.getSumAndSub(100);//× 个数不一致
        a.f3("tom", 10); //ok
        //a.f3(100, "jack"); // 实际参数和形式参数顺序不对
    }
}
class AA {
    //细节: 方法不能嵌套定义
    public void f4() {
        //错误
        // public void f5() {
        // }
    }
    public void f3(String str, int n) {
    }
    //1. 一个方法最多有一个返回值[思考,如何返回多个结果返回数组]
    public int[] getSumAndSub(int n1, int n2) {
        int[] resArr = new int[2]; //
        resArr[0] = n1 + n2;
        resArr[1] = n1 - n2;
        return resArr;
    }
    //2. 返回类型可以为任意类型,包含基本类型或引用类型(数组,对象)
    // 具体看getSumAndSub
    //
    //3. 如果方法要求有返回数据类型,则方法体中最后的执行语句必须为return 值;
    // 而且要求返回值类型必须和return 的值类型一致或兼容
    public double f1() {
        double d1 = 1.1 * 3;
        int n = 100;
        return n; // int ->double
        //return d1; //ok? double -> int
    }
    //如果方法是void,则方法体中可以没有return 语句,或者只写return ;
    //老韩提示:在实际工作中,我们的方法都是为了完成某个功能,所以方法名要有一定含义
    //,最好是见名知意
    public void f2() {
        System.out.println("hello1");
        System.out.println("hello1");
        System.out.println("hello1");
        int n = 10;
        //return ;
    }
}

3)形参列表

  • 一个方法可以有0个参数,也可以有多个参数,中间用逗号隔开
  • 参数类型可以为任意类型,包含基本类型或引用类型
  • 调用带参数的方法时,一定对应着参数列表传入相同类型或兼容类型的参数
  • 方法定义时的参数称为形式参数,简称形参;方法调用时传入的参数为实际参数,简称为实参,实参和形参的类型要一致或兼容、个数、顺序必须一致

4)方法体

  • 里面写完成功能的具体的语句,可以为输入、输出、变量、运算、分支、循环、方法调用,但里面不能再定义方法!即:方法不能嵌套定义。

成员方法调用细节说明:

  • 同一个类中的方法调用:直接调用即可
  • 跨类中的方法A调用B类方法,需要通过对象名调用,比如:对象名.方法名(参数)
  • 跨类的方法调用和方法的访问修饰符相关
public class MethodDetail02 {
    //编写一个main 方法
    public static void main(String[] args) {
        A a = new A();
        //a.sayOk();
        a.m1();
    }
}
class A {
    //同一个类中的方法调用:直接调用即可
    //
    public void print(int n) {
        System.out.println("print()方法被调用n=" + n);
    }
    public void sayOk() { //sayOk 调用print(直接调用即可)
        print(10);
        System.out.println("继续执行sayOK()~~~");
    }
    //跨类中的方法A 类调用B 类方法:需要通过对象名调用
    public void m1() {
        //创建B 对象, 然后在调用方法即可
        System.out.println("m1() 方法被调用");
        B b = new B();
        b.hi();
        System.out.println("m1() 继续执行:)");
    }
}
class B {
    public void hi() {
        System.out.println("B 类中的hi()被执行");
    }
}
成员方法的传参机制
基本数据类型的传参机制

基本数据类型,传递的是值(值拷贝),形参的改变不影响实参

public class MethodParameter01 {
    //编写一个main 方法
    public static void main(String[] args) {
        int a = 10;
        int b = 20;
        //创建AA 对象名字obj
        AA obj = new AA();
        obj.swap(a, b); //调用swap
        System.out.println("main 方法a=" + a + " b=" + b);//a=10 b=20
    }
}
class AA {
    public void swap(int a,int b){
        System.out.println("\na 和b 交换前的值\na=" + a + "\tb=" + b);//a=10 b=20
        //完成了a 和b 的交换
        int tmp = a;
        a = b;
        b = tmp;
        System.out.println("\na 和b 交换后的值\na=" + a + "\tb=" + b);//a=20 b=10
    }
}

具体分析:

image-20221013112942678

引用数据类型的传参机制

引用类型传递的是地址(传递的也是值,但是值是地址),可以通过形参影响实参

代码:

public class MethodParameter02 {
    //编写一个main 方法
    public static void main(String[] args) {
        //测试
        B b = new B();
        // int[] arr = {1, 2, 3};
        // b.test100(arr);//调用方法
        // System.out.println(" main 的arr 数组");
        // //遍历数组
        // for(int i = 0; i < arr.length; i++) {
        // System.out.print(arr[i] + "\t");
        // }
        // System.out.println();
        //测试
        Person p = new Person();
        p.name = "jack";
        p.age = 10;
        b.test200(p);
        //测试题, 如果test200 执行的是p = null ,下面的结果是10
        //测试题, 如果test200 执行的是p = new Person();..., 下面输出的是10
        System.out.println("main 的p.age=" + p.age);//10000
    }
}
class Person {
    String name;
    int age;
}
class B {
    public void test200(Person p) {
        //p.age = 10000; //修改对象属性
        //思考
        p = new Person();
        p.name = "tom";
        p.age = 99;
        //思考
        //p = null;
    }
    //B 类中编写一个方法test100,
    //可以接收一个数组,在方法中修改该数组,看看原来的数组是否变化
    public void test100(int[] arr) {
        arr[0] = 200;//修改元素
        //遍历数组
        System.out.println(" test100 的arr 数组");
        for(int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + "\t");
        }
        System.out.println();
    }
}

具体分析:

image-20221013122159264

image-20221013122213738

p = null 和 p = new Person()对应示意图:

image-20221013122229231

image-20221013122244921

练习:编写一个方法copyPerson,可以复制一个Person 对象,返回复制的对象。克隆对象, 注意要求得到新对象和原来的对象是两个独立的对象,只是他们的属性相同

//编写一个main 方法
public static void main(String[] args) {
    Person p = new Person();
    p.name = "milan";
    p.age = 100;
    //创建tools
    MyTools tools = new MyTools();
    Person p2 = tools.copyPerson(p);
    //到此p 和p2 是Person 对象,但是是两个独立的对象,属性相同
    System.out.println("p 的属性age=" + p.age + " 名字=" + p.name);
    System.out.println("p2 的属性age=" + p2.age + " 名字=" + p2.name);
    //这里老师提示: 可以同对象比较看看是否为同一个对象
    System.out.println(p == p2);//false
}
}
class Person {
    String name;
    int age;
}
class MyTools {
    //编写一个方法copyPerson,可以复制一个Person 对象,返回复制的对象。克隆对象,
    //注意要求得到新对象和原来的对象是两个独立的对象,只是他们的属性相同
    //
    //编写方法的思路
    //1. 方法的返回类型Person
    //2. 方法的名字copyPerson
    //3. 方法的形参(Person p)
    //4. 方法体, 创建一个新对象,并复制属性,返回即可
    public Person copyPerson(Person p) {
        //创建一个新的对象
        Person p2 = new Person();
        p2.name = p.name; //把原来对象的名字赋给p2.name
        p2.age = p.age; //把原来对象的年龄赋给p2.age
        return p2;
    }
}

image-20221013122312417

方法递归调用

基本介绍:

简单的说:递归就是方法自己调用自己,每次调用时传入不同的变量,递归有利于编程者解决复杂问题,使代码变得简洁

举例:
1)打印问题

2)递归问题

代码:

public class Recursion01 {
    //编写一个main 方法
    public static void main(String[] args) {
        T t1 = new T();
        t1.test(4);//输出什么? n=2 n=3 n=4
        int res = t1.factorial(5);
        System.out.println("5 的阶乘res =" + res);
    }
}
class T {
    //分析
    public void test(int n) {
        if (n > 2) {
            test(n - 1);
        }
        System.out.println("n=" + n);
    }
    //factorial 阶乘
    public int factorial(int n) {
        if (n == 1) {
            return 1;
        } else {
            return factorial(n - 1) * n;
        }
    }
}

具体分析:

image-20221013122348921

递归的重要规则:

1)执行一个方法时,就创建一个新的受保护的独立空间(栈空间)

2)方法的局部变量是独立的,不会相互影响,比如n变量

3)如果方法中使用的是引用变量类型(比如数组),就会共享该引用类型的数据

4)递归必须向退出递归的条件逼近,否则就是无限递归,出现StackOverflowError,栈溢出

5)当一个方法执行完毕,或者遇到return,就会返回,遵守谁调用,就将结果返回给谁,同时当方法执行完毕或者返回时,该方法也就执行完毕

方法重载

基本介绍:

Java中允许同一个类中,多个同名方法的存在,但要求形参列表不一致

比如:System.out.println(); out 是PrintStream 类型

重载的好处:

  • 减轻了起名的麻烦
  • 减轻了记名的麻烦

案例:

class MyCalculator {
    //下面的四个calculate 方法构成了重载
    //两个整数的和
    public int calculate(int n1, int n2) {
        System.out.println("calculate(int n1, int n2) 被调用");
        return n1 + n2;
    }
    //没有构成方法重载, 仍然是错误的,因为是方法的重复定义
    // public void calculate(int n1, int n2) {
    // System.out.println("calculate(int n1, int n2) 被调用");
    // int res = n1 + n2;
    // }
    //看看下面是否构成重载, 没有构成,而是方法的重复定义,就错了
    // public int calculate(int a1, int a2) {
    // System.out.println("calculate(int n1, int n2) 被调用");
    // return a1 + a2;
    // }
    //一个整数,一个double 的和
    public double calculate(int n1, double n2) {
        return n1 + n2;
    }
    //一个double ,一个Int 和
    public double calculate(double n1, int n2) {
        System.out.println("calculate(double n1, int n2) 被调用..");
        return n1 + n2;
    }
    //三个int 的和
    public int calculate(int n1, int n2,int n3) {
        return n1 + n2 + n2;
    }
}

注意细节:

1)方法名:必须相同

2)参数列表:必须不同(参数类型或个数或顺序,至少有一样不同,参数名无要求)

3)返回类型:无要求

练习:

/*
编写程序,类Methods 中定义三个重载方法并调用。方法名为m。
三个方法分别接收一个int 参数、两个int 参数、一个字符串参数。分别执行平方运算并输出结果,
相乘并输出结果,输出字符串信息。在主类的main ()方法中分别用参数区别调用三个方法
定义三个重载方法max(),第一个方法,返回两个int 值中的最大值,
第二个方法,返回两个double 值中的最大值,第三个方法,
返回三个double 值中的最大值,并分别调用三个方法
*/
class Methods {
    //分析
    //1 方法名max
    //2 形参(int,int)
    //3.int
    public int max(int n1, int n2) {
        return n1 > n2 ? n1 : n2;
    }
    //分析
    //1 方法名max
    //2 形参(double,double)
    //3.double
    public double max(double n1, double n2) {
        return n1 > n2 ? n1 : n2;
    }
    //分析
    //1 方法名max
    //2 形参(double,double,double)
    //3.double
    public double max(double n1, double n2, double n3) {
        System.out.println("max(double n1, double n2, double n3)");
        //求出n1 和n2 的最大值
        double max1 = n1 > n2 ? n1 : n2;
        return max1 > n3 ? max1 : n3;
    }
    public double max(double n1, double n2, int n3) {
        System.out.println("max(double n1, double n2, int n3)");
        //求出n1 和n2 的最大值
        double max1 = n1 > n2 ? n1 : n2;
        return max1 > n3 ? max1 : n3;
    }
    //分析
    //1 方法名m
    //2 形参(int)
    //3.void
    public void m(int n) {
        System.out.println("平方=" + (n * n));
    }
    //1 方法名m
    //2 形参(int, int)
    //3.void
    public void m(int n1, int n2) {
        System.out.println("相乘=" + (n1 * n2));
    }
    //1 方法名m
    //2 形参(String)
    //3.void
    public void m(String str) {
        System.out.println("传入的str=" + str);
    } 
}

可变参数

基本介绍:

Java中允许将同一个类中多个同名同功能但参数个数不同的方法,封装成一个方法

基本语法:

访问修饰符 返回类型 方法名(数据类型...形参名){
    
}

案例:

public class VarParameter01 {
    //编写一个main 方法
    public static void main(String[] args) {
        HspMethod m = new HspMethod();
        System.out.println(m.sum(1, 5, 100)); //106
        System.out.println(m.sum(1,19)); //20
    }
}
class HspMethod {
    //可以计算2 个数的和,3 个数的和, 4. 5, 。。
    //可以使用方法重载
    // public int sum(int n1, int n2) {//2 个数的和
    // return n1 + n2;
    // }
    // public int sum(int n1, int n2, int n3) {//3 个数的和
    // return n1 + n2 + n3;
    // }
    // public int sum(int n1, int n2, int n3, int n4) {//4 个数的和
    // return n1 + n2 + n3 + n4;
    // }
    //.....
    //上面的三个方法名称相同,功能相同, 参数个数不同-> 使用可变参数优化
    //1. int... 表示接受的是可变参数,类型是int ,即可以接收多个int(0-多)
    //2. 使用可变参数时,可以当做数组来使用即nums 可以当做数组
    //3. 遍历nums 求和即可
    public int sum(int... nums) {
        //System.out.println("接收的参数个数=" + nums.length);
        int res = 0;
        for(int i = 0; i < nums.length; i++) {
            res += nums[i];
        }
        return res;
    }
}   

注意事项:

1)可变参数的实参可以是0个或者任意多个

2)可变参数的实参可以是数组

3)可变参数的本质是数组

4)可变参数可以和普通类型的参数一起放在形参列表,但必须保证可变参数在最后

5)一个形参列表中只可能出现一个可变参数

作用域

基本介绍:
面向对象中,变量作用域是非常重要的知识点

  • 在Java编程中,主要的变量就是属性(成员变量)和局部变量

  • 我们说的局部变量一般是指在成员方法中定义的变量

  • Java中作用域的分类

    • 全局变量:也就是属性,作用域为整个类体
    • 局部变量:也就是除了属性之外的其他变量,作用域为定义它的代码块中
  • 全局变量可以不赋值,直接使用,因为有默认值,局部变量必须赋值后,才能使用,因为没有默认值

代码:

public class Demo03 {
    public static void main(String[] args) {

    }
}
class Dog {
    //全局变量:也就是属性,作用域为整个类体
    //属性在定义时,可以直接赋值
    int age = 10;

    //全局变量(属性)可以不赋值,直接使用,因为有默认值
    double weight; //默认值为0.0
    //局部变量必须赋值后才能使用,因为没有默认值
    public void h1(){
        int num = 1;
        System.out.println("num="+num);
    }
    public void cry(){
        //1.局部变量一般是指在成员方法中定义的变量
        //2.n和name就是局部变量
        //3.n和name的作用域在cry方法中
        int n = 10;
        String name = "jack";
        System.out.println("在cry方法中使用属性age="+age);
    }
    public void eat(){
        System.out.println("在eat方法中使用属性age="+age);
    }
}

注意事项:

1)属性和局部变量可以重名,访问时遵循就近原则

2)在同一个作用域下,比如在同一个成员方法中,两个局部变量,不能重名

3)属性生命周期较长,伴随着对象的创建而创建,伴随着对象的死亡而死亡,局部变量,生命周期较短,伴随着它的代码块的执行而创建,伴随着代码块的结束而死亡,即在一个方法调用过程中

4)作用域范围不同

  • 全局变量/属性:可以被本类使用,或其他类使用(通过对象调用)
  • 局部变量:只能在本类中对应的方法中使用

5)修饰符不同

  • 全局变量/属性可以加修饰符
  • 局部变量不可以加修饰符
public class VarScopeDetail {
    //编写一个main 方法
    public static void main(String[] args) {
        Person p1 = new Person();
        /*
属性生命周期较长,伴随着对象的创建而创建,伴随着对象的销毁而销毁。
局部变量,生命周期较短,伴随着它的代码块的执行而创建,
伴随着代码块的结束而销毁。即在一次方法调用过程中
*/
        //p1.say();//当执行say 方法时,say 方法的局部变量比如name,会创建,当say 执行完毕后
        //name 局部变量就销毁,但是属性(全局变量)仍然可以使用
        //
        T t1 = new T();
        t1.test(); //第1 种跨类访问对象属性的方式
        t1.test2(p1);//第2 种跨类访问对象属性的方式
    }
}
class T {
    //全局变量/属性:可以被本类使用,或其他类使用(通过对象调用)
    public void test() {
        Person p1 = new Person();
        System.out.println(p1.name);//jack
    }
    public void test2(Person p) {
        System.out.println(p.name);//jack
    }
}
class Person {
    //细节: 属性可以加修饰符(public protected private..)
    // 局部变量不能加修饰符
    public int age = 20;
    String name = "jack";
    public void say() {
        //细节属性和局部变量可以重名,访问时遵循就近原则
        String name = "king";
        System.out.println("say() name=" + name);
    }
    public void hi() {
        String address = "北京";
        //String address = "上海";//错误,重复定义变量
        String name = "hsp";//可以
    }
}

构造器

一个需求:前面我们在创建人类的对象时,是先把一个对象创建好后,再给他的年龄和姓名属性赋值,如果现在要求,在创建人类的对象时,就直接指定这个对象的年龄和姓名,这时候就可以使用构造器

基本语法:

[修饰符] 方法名(形参列表){
    方法体;
}

说明:

  • 构造器的修饰符可以默认,也可以是public,protected, private
  • 构造器没有返回值
  • 方法名和类名名字必须一样
  • 参数列表和成员方法一样的规则
  • 构造器的调用由系统完成

基本介绍:

构造方法又叫构造器,是类的一种特殊的方法,它的主要作用是完成对新对象的初始化

具体特点:

  • 方法名和类名相同
  • 没有返回值
  • 在创建对象时,系统会自动的调用该类的构造器完成对象的初始化
public class Constructor01 {
    //编写一个main 方法
    public static void main(String[] args) {
        //当我们new 一个对象时,直接通过构造器指定名字和年龄
        Person p1 = new Person("smith", 80);
        System.out.println("p1 的信息如下");
        System.out.println("p1 对象name=" + p1.name);//smith
        System.out.println("p1 对象age=" + p1.age);//80
    }
}
//在创建人类的对象时,就直接指定这个对象的年龄和姓名
//
class Person {
    String name;
    int age;
    //构造器
    //老韩解读
    //1. 构造器没有返回值, 也不能写void
    //2. 构造器的名称和类Person 一样
    //3. (String pName, int pAge) 是构造器形参列表,规则和成员方法一样
    public Person(String pName, int pAge) {
        System.out.println("构造器被调用~~ 完成对象的属性初始化");
        name = pName;
        age = pAge;
    }
}

注意事项:

1)一个类可以定义多个不同的构造器,即构造器重载

2)构造器名和类名要相同

3)构造器没有返回值

4)构造器是完成对象的初始化,并不是创建对象

5)在创建对象时,系统自动的调用该类的构造方法

6)如果程序员没有定义构造器,系统会自动给类生成一个默认无参构造器(也叫默认构造器)

7)一旦定义了自己的构造器,默认的构造器就覆盖了,就不能再使用默认的无参构造器,除非显式的定义一下

class Dog {
    //如果程序员没有定义构造器,系统会自动给类生成一个默认无参构造器(也叫默认构造器)
    //使用javap 指令反编译看看
    /*
默认构造器
Dog() {
}
*/
    //一旦定义了自己的构造器,默认的构造器就覆盖了,就不能再使用默认的无参构造器,
    //除非显式的定义一下,即: Dog(){} 写(这点很重要)
    //
    public Dog(String dName) {
        //...
    }
    Dog() { //显式的定义一下无参构造器
    }
}

对象创建流程

一个案例:

class Person{
    int age = 90;
    String name;
    Person(String n,int a){  //构造器
        name = n; //给属性赋值
        age = a; 
    }
}

image-20221013122502705

具体分析:

1)加载Person类信息(Person.class),只会加载一次

2)在堆中分配空间(地址)

3)完成对象初始化

  • 默认初始化 age = 0,name = null
  • 显示初始化 age=90 name = null
  • 构造器的初始化:age = 20,name =“小倩”

4)在对象在堆中的地址,返回给p(p是对象名,也可以理解为对象的引用)

this关键字

问题引入:

image-20221013122710217

class Dog1{
    String name;
    int age;
//    public Dog1(String dName,int dage){ //构造器
//        name = dName;
//        age = dage;
//    }
    //如果我们出现构造器的形参,能够直接写成属性名,就更好了
    //但是出现了一个问题,根据变量的作用域原则
    //构造器的name是局部变量,而不是属性
    //构造器的age 是局部变量,而不是属性
    //==>引出this关键字来解决
    public Dog1(String name,int age)
    {
        name = name;
        age = age;
    }
       

基本介绍:
Java虚拟机会给每个对象分配this,代表当前对象

简单的说:哪个对象调用,this就代表哪个对象

理解:

class Dog{
    public String name;
    public int age;
    public Dog(String name,int in_age){
        this.name = name;
        this.age = in_age;
    }
    public void info(){
        System.out.println(this.name+"\t"+this.age+"\t"+"当前对象的hashcode是:"+this.hashCode());
    }
    
}

image-20221013122730209

注意事项:
1)this关键字可以用来访问本类的属性、方法、构造器

2)this用于区分当前类的属性和局部变量

3)访问成员方法的语法:this.方法名(参数列表)

4)访问构造器语法:this(参数列表):注意只能在构造器中使用,即只能在构造器中访问另外一个构造器,必须放在第一条语句

5)this不能在类定义的外部使用,只能在类定义的方法中使用

public class Demo05 {
    public static void main(String[] args) {
//        T t1 = new T();
//        t1.f2();
        T t2 = new T();

    }
}
class T{
    String name = "jack";
    int num = 100;
    /*
    细节:访问构造器语法:this(参数列表);
    注意只能在构造器中使用(即只能在构造器中访问另外一个构造器)
    注意:如果有this(参数列表),必须放在第一条语句
     */
    public T(){
        this("jack",100);
        //这里去访问T(String name,int age)
        System.out.println("T()构造器");
    }
    public T(String name,int age){
        System.out.println("T(String name,int age)构造器");
    }
    //this关键字可以用来访问本类的属性
    public void f3(){
        String name = "smith";
        //传统方式
        System.out.println("name="+name+"num="+num);//根据就近原则,输出smith,100
        //也可以使用this访问属性
         System.out.println("name="+this.name+"num="+this.num);//输出jack,100
    }
    //访问成员方法的语法:this.方法名(参数列表)
    public void f1(){
        System.out.println("f1()方法...");
    }
    public void f2(){
        System.out.println("f2()方法...");
        //调用本类的f1
        //第一种方式
        f1();
        //第二种方式
        this.f1();
    }
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值