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

java学习笔记5

学习面向对象内容的三条主线

1.Java 类及类的成员
2. 面向对象的三大特征
3. 其它关键字

interests方法调用完之后的返回值info,info返回的地方是main方法里面

GC回收,垃圾回收机制

ctrl+alt+shift换行,向上换行

回车就是输出语句:

System.out.println(students[i].show());

面向过程与面向对象

1.面向对象内容的三条主线:
- Java类及类的成员:(重点)属性、方法、构造器;(熟悉)代码块、内部类
- 面向对象的特征:封装、继承、多态、(抽象)
- 其他关键字的使用:this、super、package、import、static、final、interface、abstract等

2.面向过程编程(POP)  vs  面向对象编程(00P)
2.1 简单的语言描述二者的区别
> 面向过程:
- 以'函数'为组织单位。
- 是一种"`执行者思维`",适合解决简单问题。扩展能力差、后期维护难度较大。
> 面向对象:
- 以`类`为组织单位。每种事物都具备自己的`属性`和`行为/功能'。
-是一种"`设计者思维`",适合解决复杂问题。代码扩展性强、可维护性高。

2.2 二者关系:
我们千万不要把面向过程和面向对象对立起来。他们是相辅相成的。面向对象离不开面向过程!

面向对象编程的两个核心概念:类(Class)、对象(0bject)
谈谈对这两个概念的理解?
类:具有相同特征的事物的抽象描述,是`抽象的`、概念上的定义。
对象:实际存在的该类事物的`每个个体`,是`具体的`,因而也称为`实例(instance)'。

4.类的声明与使用
4.1 体会:设计类,其实就是设计类的成员
class Person {

}

4.2 类的内部成员一、二:
成员之一:属性、成员变量、field(字段、域)
成员之二:(成员)方法、函数、method


4.3 类的实例化
等价描述:类的实例化 <=> 创建类的对象 <=> 创建类的实例
格式:类类型 对象名 = 通过new创建的对象实体
举例:
Phone p1 = new Phone();
Scanner scan = new Scanner(System.in);
String str =new string();

5.面向对象完成具体功能的操作的三步流程(非常重要)
步骤1:创建类,并设计类的内部成员(属性、方法)
步骤2:创建类的对象。比如:Phone p1 = new Phone();
步骤3:通过对象,调用其内部声明的属性或方法,完成相关的功能


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

        //复习:数据类型 变量名 = 变量值
//        Scanner sc = new Scanner(System.in);
        //创建Phone的对象
        Phone p1 = new Phone();
        //通过Phone的对象,调用其内部声明的属性和方法
        //格式:"对象.属性" 或 "对象.方法"
        p1.name = "huawei mate50";
        p1.price = 6999;
        System.out.println("name = " + p1.name + " price = " + p1.price);

        //调用方法
        p1.call();
        p1.sendMessage("有内鬼,终止交易");
        p1.playGame();
    }
}

Phone 
public class Phone {
    //属性
    String name; //品牌
    double price; // 价格
    //方法
    public void call() {
        System.out.println("手机能够拨打电话");
    }
    public void sendMessage(String message) {
        System.out.println("发送信息:" + message);
    }
    public void playGame() {
        System.out.println("手机可以玩游戏");
    }
}

类和对象

内存解析

匿名对象

1.对象在内存中的分配涉及到的内存结构(理论)
- 栈(stack): 方法内定义的变量,存储在栈中
- 堆(heap):new 出来的结构,(比如:数组实体,对象的实体),包括对象中的属性
- 方法区(method area):存放类的模板,比如:Perosn类的模板

2.类中对象的内存解析
2.1 创建类的一个对象
见《01-创建类的一个对象.png》
2.2 创建类的多个对象
见《02-创建类的多个对象1.png》、《02-创建类的多个对象2.png》

强调1:创建了Person类的两个对象
Person pl=new Person(;
Person p2=new Person();

说明:创建类的多个对象时,每个对象在堆空间中有一个对象实体。每个对象实体中保存着一份类的属性。
如果修改某一个对象的某属性值时,不会影响其他对象此属性的值。
p1.age =10;
p2.age = 20;
p1.age = 30;
System.out.println(p2.age);//20

强调2:声明类的两个变量
Person p1 = new Person();
Person p3 = p1;

说明:此时的p1,p3 两个变量指向了堆空间中的同一个对象实体。(或p1,p3保存的地址值相同)
如果通过其中某一个对象变量修改对象的属性时,会影响另一个对象变量此属性的值。

p1.age = 10;
p3.age = 20;
System.out.println(p1.age);//20

2.3 对象调用方法的过程(在"03-类的成员之二:方法"中讲解)


Person 
public class Person {
    //属性
    String name; //姓名
    int age; // 年龄
    char gender; // 性别

    //方法
    public void eat() {
        System.out.println("人吃饭");
    }
    public void sleep(int hour) {
        System.out.println("人至少保证明天" + hour + "小时的睡眠");
    }
    public void interests(String hobby) {
        System.out.println("我的爱好是:" + hobby);
    }
}


PersonTest 
public class PersonTest {
    public static void main(String[] args) {
        //创建对象,类的实例化
        Person p1 = new Person();
        //通过对象调用属性和方法
        p1.name = "杰克";
        p1.age = 18;
        p1.gender = '男';
        System.out.println("name = " + p1.name + ", age = " + p1.age + ", gender = " + p1.gender);
        p1.eat();
        p1.sleep(8);
        p1.interests("画画");

        //在创建一个Person类的实例
        Person p2 = new Person();
        p2.name = "露丝";
        p2.age = 24;
        p2.gender = '女';
        System.out.println("name = " + p2.name + ", age = " + p2.age + ", gender = " + p2.gender);

    }
}

类的成员之一: 属性(field)

对象属性的默认初始化赋值
类的成员之一:属性

1.变量的分类:
- 角度一:按照数据类型来分:基本数据类型(8种)、引用数据类型(数组、类、接口、枚举、注解、记录)
- 角度二:按照变量在类中声明的位置的不同:成员变量(或属性)、局部变量(方法内、方法形参、构造器内、构造器形参、代码块内等)
2.属性的几个称谓: 成员变量、属性、field(字段、域)

3.区分成员变量  vs  局部变量
3.1 相同点:
> 变量声明的格式相同:数据类型 变量名 = 变量值
> 变量都有其有效的作用域。出了作用域,就失效了。
> 变量必须先声明,后赋值,再使用。

3.2 不同点:
① 类中声明的位置的不同:
    属性:声明在类内,方法外的变量
    局部变量:声明方法、构造器内部的变量
② 在内存中分配的位置不同(难):
    属性:随着对象的创建,存储在堆空间中。
    局部变量:存储在栈空间中
③ 生命周期:
    属性:随着对象的创建而创建,随着对象的消亡而消亡。
    局部变量:随着方法对应的栈帧入栈,局部变量会在栈中分配;随着方法对应的栈帧出栈,局部变量消亡
④ 作用域:
    属性:在整个类的内部都是有效的
    局部变量:仅限于声明此局部变量所在的方法(或构造器、代码块)中
⑤ 是否可以有权限修饰符进行修饰:(难)
    都有哪些权限修饰符:public、protected、缺省、private。(用于表明所修饰的结构可调用的范围的大小)
    属性,是可以使用权限修饰符进行修饰的。暂时还未讲封装性,所以大家先不用写任何权限符。
    而局部变量,不能使用任何权限修饰符进行修饰的。
⑥ 是否有默认值:(重点)
    属性:都有默认初始化值
        意味着,如果没有给属性进行显式初始化赋值,则会有默认初始化值。
    局部变量:都没有默认初始化值
        意味着,在使用局部变量之前,必须要显式的赋值,否则报错。
        注意:对于方法的形参而言,在调用方法时,给此形参赋值即可。



类的成员之二:方 法(method)

类的成员之二:方法(method)
1.使用方法的好处
方法的理解:`方法`是类或对象行为特征的抽象,用来完成某个功能操作。
方法的好处:实现代码重用,减少冗余,简化代码

2.使用举例
- Math.random()的random()方法
- Math.sqrt(x)的sqrt(x)方法
- System.out.println(x)的println(x)方法
- new Scanner(System.in).nextInt()的nextInt()方法
- Arrays类中的binarySearch()方法、sort()方法、equals()方法

3.声明举例
public void eat()
public void sleep(int hour)
public String interests(String hobby)
public int age()

4.方法声明的格式(重要)
权限修饰符 [其它修饰符] 返回值类型 方法名(形参列表) [throws 异常类型]{//方法头
    //方法体
}

注:[]中的内部不是必须的,以后再讲。

5.具体的方法声明的细节
5.1 权限修饰符
    ① Java规定了哪些权限修饰符呢?有四种:private \ 缺省 \ protected \ public(放到封装性讲)
        暂时大家声明方法时,可以先都写成public的。

5.2 返回值类型:描述当调用完此方法时,是否需要返回一个结果。
    分类:
        > 无返回值类型:使用void表示即可。比如:System.out.println(x)的println(x)方法、Arrays的sort()
        > 有具体的返回值类型:需要指明返回的数据的类型。可以是基本数据类型,也可以引用数据类型
            > 需要在方法内部配合使用"return+返回值类型的变量或常量"
            比如:Math.random()、new Scanner(System.in).nextInt()等

    [经验]我们在声明方法时,要不要提供返回值类型呢?
        > 根据方法具体实现的功能来决定。换句话说,具体问题具体分析
        > 根据题目要求

5.3 方法名:属于标识符。需要满足标识符的规定和规范。“见名知意"
5.4 形参列表:形参属于局部变量,且可以声明多个。
    格式:(形参类型1 形参1,形参类型2 形参2,..)
    分类:无形参列表、有形参列表
        > 无形参列表:不能省略一对()。比如:Math.random()、new Scanner(System.in).nextInt()
        > 有形参列表:根据方法调用时,需要的不确定的变量的类型和个数,确定形参的类型和个数。
            比如:Arrays类中的binarySearch()方法、sort()方法、equals()方法

        [经验]我们在声明方法时,是否需要形参列表呢?
                > 根据方法具体实现的功能来决定。换句话说,具体问题具体分析
                > 根据题目要求
5.5 方法体:当我们调用一个方法时,真正执行的代码。体现了此方法的功能。

6.往意点
> Java里的方法'不能独立存在’,所有的方法必须定义在类里
> Java中的方法不调用,不执行。每调用一次,就执行一次。
> 方法内可以调用本类中的(其它)方法或属性
> 方法内不能定义方法。

7.关键字:return
7.1 return的作用
    - 作用1:结束一个方法
    - 作用2:结束一个方法的同时,可以返回数据给方法的调用者(方法声明中如果有返回值类型,则方法内需要搭配return使用)

7.2 使用注意点:
return后面不能声明执行语句。

8.方法调用的内存解析:
- 形参:方法在声明时,一对()内声明的一个或多个形式参数,简称为形参。
- 实参:方法在被调用时,实际传递给形参的变量或常量,就称为实际参数,简称实参。

过程概述:

练习:


案例:将属性测试的exer1中关于员工信息的输出内容放到方法中。通过调用方法显示。
EmployeeTest 
public class EmployeeTest {
    public static void main(String[] args) {
        //创建类的实例(或创建类的对象,类的实例化)
        Employee emp1 = new Employee();
        System.out.println(emp1); //地址值
        emp1.id = 1001;
        emp1.name = "Tom";
        emp1.age = 24;
        emp1.salary = 7800;
        //替换为:
        emp1.show();

//        System.out.println(emp1.show()); // 编译报错
        //换一种写法,通过输出返回值的方式打印信息
        System.out.println(emp1.show1()); //编译通过
//        System.out.println("id = " + emp1.id + " name = " + emp1.name + " age = " + emp1.age + " salary = " + emp1.salary);
        //创建Employee的第2个对象
//        Employee emp3 = emp1; //错误写法
        Employee emp2 = new Employee();
        emp2.id = 1002;
        //替换为:
        emp2.show();
//        System.out.println("id = " + emp2.id + " name = " + emp2.name + " age = " + emp2.age + " salary = " + emp2.salary);


    }
}

Employee 
public class Employee {
    //属性(或成员变量)
    int id; //编号
    String name; //姓名
    int age; //年龄
    double salary; //薪资
    //定义一个方法,用于显示员工的属性信息
    public void show() {
        System.out.println("id = " + id + " name = " + name + " age = " + age + " salary = " + salary);
    }
    public String show1() {
        return "id = " + id + " name = " + name + " age = " + age + " salary = " + salary;
    }
}

练习2:

对象数组
1.何为对象数组?如何理解?
数组的元素可以是基本数据类型,也可以是引用数据类型。当元素是引用类型中的类时,我们称为对象数组。
2 举例:
string[],Person[],Student[],Customer[]等
3.案例:
1)定义类Student,包含三个属性:学号number(int),年级state(int),成绩score(int)。
2)创建20个学生对象,学号为1到20,年级和成绩都由随机数确定。
问题一:打印出3年级(state值为3)的学生信息。
问题二:使用冒泡排序按学生成绩排序,并遍历所有学生信息
提示:
1)生成随机数:Math.random(),返回值类型double;
2)四舍五入取整:Math.round(double d),返回值类型long。
年级[1,6]:(int)(Math.random()*6+1)
分数[0,100]:(int)(Math.random()*101)


StudentTest 
public class StudentTest {
    public static void main(String[] args) {
        //创建Student[]
        Student[] students = new Student[20]; //String[] strs = new String[];
        //使用循环,给数组的元素赋值
//        Student stu = new Student(); //这是错误的,这样会把相同的地址赋值给每一个学生对象
//        for (int i = 0; i < students.length; i++) {
//            students[i] = new Student();
//        }
        //给每一个学生对象number,state,score属性赋值
        for (int i = 0; i < students.length; i++) {
            students[i] = new Student(); //与上面循环new出来的一样,如果不new的话就会报错NullPointerException
            //给每一个学生对象的number,state,score属性赋值
            students[i].number = i+1;
            students[i].state = (int)(Math.random()*6+1);
            students[i].score = (int)(Math.random()*101);
        }
        //需求1:打印出3年级(state值为3)的学生信息。
        StudentUtil util = new StudentUtil();
        util.printStudentsWithState(students, 3);
//        for (int i = 0; i < students.length; i++) {
//            if(3 == students[i].state){
//                //也可以这样写
//                Student stu = students[i];
                System.out.println(students[i].number+" "+students[i].state+" "+students[i].score);
                System.out.println("number: " + stu.number + " state: " + stu.state + " score: " + stu.score);
//                System.out.println(stu.show());
//            }
//        }
        System.out.println();
        //需求2:使用冒泡排序按学生成绩排序,并遍历所有学生信息
        //排序前遍历
        util.printStudents(students);
//        for (int i = 0; i < students.length; i++) {
//            System.out.println(students[i].show());
//        }
        System.out.println("**********************");
        util.sortStudents(students);
        //        for(int i = 0; i < students.length-1; i++){
//            for(int j = 0; j < students.length-1-i; j++){
//                if(students[j].score > students[j+1].score){
//                    //错误的,不满足实际的需求,交换的是两个学生的成绩
                    int temp = students[j].score;
                    students[j].score = students[j+1].score;
                    students[j+1].score = temp;
//                    //正确的
//                    Student temp = students[j];
//                    students[j] = students[j+1];
//                    students[j+1] = temp;
//                }
//            }
//        }
        //排序后的遍历
        util.printStudents(students);
//        for (int i = 0; i < students.length; i++) {
//            System.out.println(students[i].show());
//        }
    }
}

Student 
public class Student {
    //属性
    int number; //学号
    int state; //年级
    int score; //成绩
    public String show() {
        return "number: " + number + " state: " + state + " score: " + score;
    }
}

StudentUtil 
public class StudentUtil {
    /**
     * 打印出指定年级的学生信息
     */
    public void printStudentsWithState(Student[] students,int state) {
        for (int i = 0; i < students.length; i++) {
            if(state == students[i].state){
                Student stu = students[i];
                System.out.println(stu.show());
            }
        }
    }
    /**
     * 遍历指定的学生数组
     */
    public void printStudents(Student[] students) {
        for (int i = 0; i < students.length; i++) {
            System.out.println(students[i].show());
        }
    }

    /**
     * 针对学生数组,按照score属性从低到高排序
     * @param students
     */
    public void sortStudents(Student[] students) {
        for(int i = 0; i < students.length-1; i++){
            for(int j = 0; j < students.length-1-i; j++){
                if(students[j].score > students[j+1].score){
                    //错误的,不满足实际的需求,交换的是两个学生的成绩
//                    int temp = students[j].score;
//                    students[j].score = students[j+1].score;
//                    students[j+1].score = temp;
                    //正确的
                    Student temp = students[j];
                    students[j] = students[j+1];
                    students[j+1] = temp;
                }
            }
        }
    }
}

练习3:

案例:
根据上一章数组中的常用算法操作,自定义一个操作int[]的工具类。
涉及到的方法有:求最大值、最小值、总和、平均数、遍历数组、复制数组、数组反转、数组排序(默认从小到大排序)、查找等

MyArraysTest 
public class MyArraysTest {
    public static void main(String[] args) {
        MyArrays arrs = new MyArrays();
        int[] arr = new int[]{34,56,223,2,56,67,778,45};

        //求最大值
        System.out.println("最大值为:"+arrs.getMax(arr)); //最大值为:778
        //求平均值
        System.out.println("平均值为"+arrs.getAvg(arr)); //平均值为157
        //遍历
        arrs.print(arr); //[34,56,223,2,56,67,778,45]
        //查找
        int index = arrs.linearSearch(arr,34);
        if(index >= 0){
            System.out.println("找到了,位置为:"+index);
        }else {
            System.out.println("未找到");
        } //找到了,位置为:0
        //排序
        arrs.sort(arr);
        arrs.print(arr);//[2,34,45,56,56,67,223,778]
    }
}

MyArrays 
public class MyArrays {
    /**
     * 获取int[] 的最大值
     * @param arr 要获取最大值的数组
     * @return 数组的最大值
     */
    public int getMax(int[] arr) {
        int max = arr[0];
        for (int i = 0; i < arr.length; i++) {
            if(max < arr[i]){
                max = arr[i];
            }
        }
        return max;
    }

    /**
     * 获取int[] arr的最小值
     * @param arr 要获取最小值的数组
     * @return 数组的最小值
     */
    public int getMin(int[] arr) {
        int min = arr[0];
        for (int i = 0; i < arr.length; i++) {
            if(min > arr[i]){
                min = arr[i];
            }
        }
        return min;
    }
    public int getSum(int[] arr) {
        int sum = 0;
        for (int i = 0; i < arr.length; i++) {
            sum += arr[i];
        }
        return sum;
    }
    public int getAvg(int[] arr) {
        return getSum(arr) / arr.length;
    }
    public void print(int[] arr) { //[12,231,34]
        System.out.print("[");
        for (int i = 0; i < arr.length; i++) {
            if(i == 0){
                System.out.print(arr[i]);
            }else{
                System.out.print(","+arr[i]);
            }
        }
        System.out.print("]");
        System.out.println();
    }
    public int[] copy(int[] arr) {
        int[] newArr = new int[arr.length];
        for (int i = 0; i < arr.length; i++) {
            newArr[i] = arr[i];
        }
        return newArr;
    }
    public void reverse(int[] arr) {
        for(int i = 0, j = arr.length-1; i<j;i++,j--){
            //交换arr[i]与arr[j]位置的元素
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
    }
    public void sort(int[] arr) {
        for (int j = 0; j < arr.length-1; j++) {
            //里面只交换了一次,找出了最大值,但是一共需要交换arr.length-1轮
            //arr.length-1-j 找出的值不需要再进行对比
            for(int i = 0; i<arr.length-1-j;i++){
                if(arr[i] > arr[i+1]){
                    //交换arr[i]和arr[i+1]
                    int temp = arr[i];
                    arr[i] = arr[i+1];
                    arr[i + 1] = temp;
                }
            }
        }
    }

    /**
     * 使用线性查找的算法,查找指定的元素
     * @param arr 待查找的数组
     * @param target 要查找的元素
     * @return target元素在arr数组中的索引位置,若未找到,则返回-1
     */
    public int linearSearch(int[] arr,int target) {
        for (int i = 0; i < arr.length; i++) {
            if(arr[i] == target){
                return i;
            }
        }
        //只要代码执行到此位置,一定是没找到
        return -1;
    }
}

练习4:

案例:
(1)声明一个MyDate类型,有属性:年year,月month,日day
(2)声明一个Employee类型,包含属性:编号、姓名、年龄、薪资、生日(MyDate类型)
(3)在EmployeeTest测试类中的main()中,创建两个员工对象,并为他们的姓名和生日赋值,并显示

Employee 
public class Employee {
    //属性(或成员变量)
    int id; //编号
    String name; //姓名
    int age; //年龄
    double salary; //薪资
    MyDate birthday; //生日
}

MyDate 
public class MyDate {
    int year; //年
    int month; //月
    int day; //日
}

EmployeeTest 
public class EmployeeTest {
    public static void main(String[] args) {
        //创建employee的实例
        Employee emp1 = new Employee();

        emp1.id = 1001;
        emp1.name = "杰克";
//        emp1.name = new String("杰克");
        emp1.age = 24;
        emp1.salary = 8900;
        emp1.birthday = new MyDate();
        emp1.birthday.year = 1998;
        emp1.birthday.month = 2;
        emp1.birthday.day = 28;
        /*
        * 另一种写法
        * MyDate mydate1 = new MyDate();
        * emp1.birthday = mydate1;
        * */
        //打印员工信息
        System.out.println("id = " + emp1.id + " name = " + emp1.name + " age = " + emp1.age + " salary = " + emp1.salary + " birthday = [" + emp1.birthday.year + "年" + emp1.birthday.month + "月" + emp1.birthday.day + "日" + "]");
        //id = 1001 name = 杰克 age = 24 salary = 8900.0 birthday = [1998年2月28日]
    }
}

再谈方法

4.6.1 方法的重载
4.6.2 可变形参的方法
4.6.3 方法参数的值传递机制
4.6.4 递归方法

方法的重载(overload)

public class OverloadExer {
//    练习2:
//    编写程序,定义三个重载方法并调用。方法名为m0L。
//    三个方法分别接收一个int参数、两个int参数、一个字符串参数。
//    分别执行平方运算并输出结果,相乘并输出结果,输出字符串信息。
    public void m0L(int num){
        System.out.println(num*num);
    }
    public void m0L(int num1,int num2){
        System.out.println(num1*num2);
    }
    public void m0L(String message){
        System.out.println(message);
    }
//    练习3:
//    定义三个重载方法max()
//    第一个方法求两个int值中的最大值
//    第二个方法求两个double值中的最大值
//    第三个方法求三个double值中的最大值,并分别调用三个方法。
    public int max(int i,int j) {
        return i>j?i:j;
    }
    public double max(double d1,double d2) {
        return d1>d2?d1:d2;
    }
    public double max(double d1,double d2,double d3) {
//        double tempMax = max(d1,d2);
//        return max(tempMax, d3);
        return (max(d1,d2) > d3) ? max(d1,d2) : d3;
    }

}

//面试题
public class InterviewTest {
    public static void main(String[] args){
        int[] arr = new int[]{1,2,3};
        System.out.println(arr); //地址值
        char[] arr1 = new char[]{'a','b','c','d','e'};
        System.out.println(arr1); //abc,char[]数组的答应会将数组遍历出来
        boolean[] arr2 =new boolean[]{false,true,true};
        System.out.println(arr2);//地址值
        //println有一系列重载的方法
    }
}

可变个数的形参

可变形参的方法练习:

n个字符串进行拼接,每一个字符串之间使用某字符进行分割,如果没有传入字符串,那么返回空字符串""

public class StringConCatTest {
    public static void main(String[] args) {
        StringConCatTest test = new StringConCatTest();
        String info = test.concat("-","hello","world");
        System.out.println(info); //hello-world
        System.out.println(test.concat("/", "hello")); //hello
    }
    //n个字符串进行拼接,每一个字符串之间使用某字符进行分割,如果没有传入字符串,那么返回空字符串""
    public String concat(String operator, String ... strs){
        String result = "";
        for(int i = 0;i < strs.length;i++){
            if(i==0){
                result += strs[i];
            }else {
                result +=  operator + strs[i];
            }
        }
        return result;
    }
}
public class ArgsTest {
    public static void main(String[] args) {
        ArgsTest test = new ArgsTest();
        test.print();
        test.print(1);
        test.print(1,2);
        test.print(new int[]{1,2,3});
//        test.print(1,2,3);
    }
    public void print(int ... nums){ //编译的时候会当成一个数组来编译
        System.out.println("1111");
        for (int i = 0; i < nums.length; i++) {
            System.out.println(nums[i]);
        }
    }
//    public void print(int[] nums){
//        for (int i = 0; i < nums.length; i++) {
//            System.out.println(nums[i]);
//        }
//    }
    public void print(int i,int ... nums){

    }
    //编译不通过
//    public void print(int ... nums,int i){
//
//    }
    public void print(int i) {
        System.out.println("2222");
    }
    public void print(int i,int j){
        System.out.println("3333");
    }
    /*
     *  场素举例:
     * String sql="update customers set name =?,email = ? where id = ?";
     * String sql1 ="update customers set name = ? where id = ?";
     * public void update(String sql,0bject ... objs);
     *
    * */
}

方法参数的值传递机制

再谈方法之3:方法的值传递机制

1.(复习)对于方法内声明的局部变量来说:
如果是基本数据类型的变量,则将此变量保存的数据值传递出去
如果是引用数据类型的变量,则将此变量保存的地址值传递出去。

2.方法的参数的传递机制:值传递机制

2.1 概念(复习)
形参:在定义方法时,方法名后面括号()中声明的变量称为形式参数,简称形参。
实参:在调用方法时,方法名后面括号()中的使用的值/变量/表达式称为实际参数,简称实参。

2.2 规则
    > 如果形参是基本数据类型的变量,则将实参保存的数据值赋给形参。
    > 如果形参是引用数据类型的变量,则将实参保存的地址值赋给形参。


3.面试题:Java中的参数传递机制是什么? 值传递。(不是引用传递)



练习1: 

public class ValueTransferTest {
    public static void main(String[] args) {
        //1.基本数据类型的局部变量
        int m = 10;
        int n = m; //传递的是数据值
        System.out.println("m = " + m + "n = " + n);
        m++;
        System.out.println("m = " + m + "n = " + n);

        //2.引用数据类型的局部变量
        //2.1数组类型
        int[] arr1 = new int[]{1,2,3,4,5};
        int[] arr2 = arr1; //传递的是地址值
        arr2[0] = 10;
        System.out.println(arr1[0]); // 10

        //2.2 对象类型
        Order order1 = new Order();
        order1.orderId = 1001;

        Order order2 = order1; //传递的是地址值
        order2.orderId = 1002;

        System.out.println(order1.orderId); //1002
    }

}
class Order {
    int orderId;
}

练习2: 

public class ValueTransferTest1 {
    public static void main(String[] args) {
        ValueTransferTest1 test = new ValueTransferTest1();
        //1.对于基本数据类型的变量来说
        int m = 10;
        test.method1(m);
        System.out.println("m = " + m); //10
        //2.对于引用类型的数据变量来说
        Person p = new Person();
        p.age = 10;
        test.method2(p);
        System.out.println(p.age); //11
    }
    public void method1(int m) {
        m++;
    }
    public void method2(Person p){
        p.age++;
    }
}
class Person {
    int age;
}

练习3: 

public class ValueTransferTest2 {
    public static void main(String[] args) {
        ValueTransferTest2 test = new ValueTransferTest2();
        int m = 10;
        int n = 20;
        System.out.println("m = " + m + ", n = " + n);
        //交换两个变量的值
//        int temp = m;
//        m = n;
//        n = temp;
        //调用方法
        test.swap(m, n);
        System.out.println("m = " + m + ", n = " + n);
    }
    //方法
    public void swap(int m, int n) {
        //交换两个变量的值
        int temp = m;
        m = n;
        n = temp;//改变的只是swap当中的变量
    }
}

练习4: 

public class ValueTransferTest3 {
    public static void main(String[] args) {
        Data data = new Data();
        ValueTransferTest3 test = new ValueTransferTest3();
        data.m = 10;
        data.n = 20;
        System.out.println("m = " + data.m + ", n = " + data.n);//m = 10, n = 20
        //操作1
//        int temp = data.m;
//        data.m = data.n;
//        data.n = temp;
        //操作2
        test.swap(data);
        System.out.println("m = " + data.m + ", n = " + data.n); //m = 20, n = 10
    }
    public void swap(Data data){
        int temp = data.m;
        data.m = data.n;
        data.n = temp;
    }
}
class Data{
    int m;
    int n;
}

练习5:重写排序(根据传递的参数来确定是排序还是降序,交换的部分可以重新定义一个方法来交换,注意交换的是数组,通过地址值找到实例对象来交换数据当中的内容,注意有传递的参数为null的情况)

    /**
     * 对于数组进行排序操作
     * @param arr 待排序的数组
     * @param sortMethod  asc:升序    desc:降序
     */
    public void sort(int[] arr,String sortMethod) {
        if("asc".equals(sortMethod)){ //if(sortMethod.equals("asc")){ //升序
            for (int j = 0; j < arr.length-1; j++) {
                //里面只交换了一次,找出了最大值,但是一共需要交换arr.length-1轮
                //arr.length-1-j 找出的值不需要再进行对比
                for(int i = 0; i<arr.length-1-j;i++){
                    if(arr[i] > arr[i+1]){
                        //交换arr[i]和arr[i+1]
//                        int temp = arr[i];
//                        arr[i] = arr[i+1];
//                        arr[i + 1] = temp;\
                        //错误的:
//                        swap(arr[i], arr[i+1]);
                        //正确的:
                        swap(arr,i,i+1);
                    }
                }
            }
        }else if("desc".equals(sortMethod)){
            for (int j = 0; j < arr.length-1; j++) {
                for(int i = 0; i<arr.length-1-j;i++){
                    if(arr[i] < arr[i+1]){
                        //交换arr[i]和arr[i+1]
//                        int temp = arr[i];
//                        arr[i] = arr[i+1];
//                        arr[i + 1] = temp;
                        //错误的:
//                        swap(arr[i], arr[i+1]);
                        //正确的:
                        swap(arr,i,i+1);
                    }
                }
            }
        }else{
            System.out.println("你输入的排序方式有误");
        }
    }
//    public void swap(int i,int j){
//        int temp = i;
//        i = j;
//        j = temp;
//    }
    public void swap(int[] arr, int i, int j){
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
    }

调用
        //排序
//        arrs.sort(arr,null); //会出现空指针异常,后面的参数为null的话
//        arrs.sort(arr, "asc");
        arrs.sort(arr,"desc");

练习6: 

1.定义一个Circle类,包含一个double型的radius属性代表圆的半径,一个findArea()方法返回圆的面积。

2.定义一个类Pass0bject,在类中定义一个方法printreas(),该方法的定义如下:
public void printAreas(Circle c,int time)。

3.在printAreas方法中打印输出1到time之间的每个整数半径值,以及对应的面积。
    例如,time为5,则输出半径1,2,3,4,5,以及对应的圆面积。

4.在main方法中调用printAreas()方法,调用完毕后输出当前半径值。程序运行结果如图所示。
PassObject 
public class PassObject {
    public static void main(String[] args) {
        PassObject p = new PassObject();
        Circle circle = new Circle();
        p.PassObject(circle, 5);
        System.out.println("now radius is:" + circle.radius);
    }
    public void PassObject(Circle c, int time) {
        System.out.println("Radius\t\tArea");
        int i = 1;
        for (; i <= time; i++) {
            c.radius = i;
            System.out.println(c.radius + "\t\t\t" +c.findArea());
        }
        c.radius = i;
    }
}

Circle 
public class Circle {
    double radius; //半径

    public double findArea() {
        return Math.PI * radius * radius;
    }
}

练习7:

递归(recursion)方法


1.何为递归方法?方法自己调用自己的现象就称为递归。

2.递归方法分类:直接递归、间接递归

3.使用说明:
- 递归方法包含了一种`隐式的循环`,
- 递归方法会`重复执行`某段代码,但这种重复执行无须循环控制。
- 递归一定要向`已知方向`递归,否则这种递归就变成了无穷递归,停不下来,类似于`死循环`。最终发生`栈内存溢出`

注意:
递归调用会占用大量的系统堆栈,内存耗用多,在递归调用层次多时速度要比循环`慢的多
所以在使用递归时要慎重。
在要求高性能的情况下尽量避免使用递归,递归调用既花时间又`耗内存`。考虑使用循环选代

练习1:


练习1:
已知一个数列:f(20)=1,f(21)=4,f(n+2)= 2*f(n+1)+f(n)
其中n是大于0的整数,求f(10)的值。

练习2:
己知有一个数列:f(0)=1,f(1)= 4,
f(n+2)=2*f(n+1) + f(n),其中n是大于0的整数,求f(10)的值


public class RecursionExer01 {

//    练习1:
//    已知一个数列:f(20)=1,f(21)=4,f(n+2)= 2*f(n+1)+f(n)
//    其中n是大于0的整数,求f(10)的值。
    public int f(int n){
        if(n == 20){
            return 1;
        }else if(n == 21){
            return 4;
        }else{
            //正确的
            return f(n+2) - 2*f(n+1);
            //错误的
//            return 2*f(n-1) + f(n-2);
        }
    }
//    练习2:
//    己知有一个数列:f(0)=1,f(1)= 4,
//    f(n+2)=2*f(n+1) + f(n),其中n是大于0的整数,求f(10)的值
    public int func(int n){
        if(n == 0){
            return 1;
        }else if(n==2){
            return 4;
        }else {
            //错误的
//            return func(n+2) - 2*f(n+1);
            //正确的
            return 2*func(n-1) + func(n-2);
        }
    }
}

练习2:


案例:不死神兔
用递归实现不死神兔:故事得从西元1202年说起,话说有一位意大利青年,名叫斐波那契(Fibonacci)
在他的一部著作中提出了一个有趣的问题:假设一对刚出生的小兔一个月后就能长成大兔,
再过一个月就能生下一对小兔,并且此后每个月都生一对小兔,没有发生死亡,
问:现有一对刚出生的兔子2年后(24个月)会有多少对兔子?

月份          1     2     3    4    5
兔子对数       1     1     2    3    5

拓展:走台阶问题
假如有10阶楼梯,小朋友每次只能向上走1阶或者2阶,请问一共有多少种不同的走法呢?
阶数   1    2   3   4  ....
走法   1    2   3   5  ....
从n为3开始:
f(n) = f(n-1) + f(n-2)
【奇妙的属性】随着数列的增加,婓波那契数列前一个数与后一个数的比值越来越逼近黄金分割的数值0.618。


public class RabbitExer {
    public static void main(String[] args) {
        RabbitExer exer = new RabbitExer();
        int month = 24;
        System.out.println("兔子的对数为:"+exer.getRabbitNumber(month)); //46368
    }
    public int getRabbitNumber(int month){
        if(month == 1){
            return 1;
        }else if(month == 2){
            return 1;
        }else {
            return getRabbitNumber(month-1) + getRabbitNumber(month-2);
        }
    }
}

关键字:package、 import的使用

练习总结,参考import的注意事项来导入包的情况示例: 

一、package关键字的使用
1.说明
> package:包
- package用于指明该文件中定义的类、接口等结构所在的包
- 一个源文件只能有一个声明包的package语句
- package语句作为Java源文件的第一条语句出现。若缺省该语句,则指定为无名包。
- 包名,属于标识符,满足标识符命名的规则和规范(全部小写)、见名知意
    - 包通常使用所在公司域名的倒置:com.atguigu.xxx。
    - 大家取包名时不要使用"java.xx'"包
- 包对应于文件系统的目录,package语句中用"."来指明包(目录)的层次,每.一次就表示一层文件目录。
- 同一个包下可以声明多个结构(类、接口),但是不能定义同名的结构(类、接口)。不同的包下可以定义同名的结构(类、接口)
> package用于指明该文件中定义的类、接口等结构所在的包
2.包的作用
- 包可以包含类和子包,划分`项目层次`,便于管理
- 帮助`管理大型软件`系统:将功能相近的类划分到同一个包中。比如:MVC的设计模式
- 解决`类命名冲突`的问题
- 控制`访问权限

3.JDK中主要的包
1. java.lang----包含一些Java语言的核心类,如String、Math、Integer、 System和Thread,提供常用功能
2. java.net----包含执行与网络相关的操作的类和接口。
3. java.io ----包含能提供多种输入/输出功能的类。
4. java.util----包含一些实用工具类,如定义系统特性、接口的集合框架类、使用与日期日历相关的函数。
5. java.text----包含了一些java格式化相关的类
6. java.sql----包含了java进行JDBC数据库编程的相关类/接口
7. java.awt----包含了构成抽象窗口工具集(abstract window toolkits)的多个类,这些类被用来构建和管理应用程序的图形用户界面(GUI)。 B/S C/S

二、import关键字的使用
import:导入
为使用定义在不同包中的Java类,需用import语句来引入指定包层次下所需要的类
或全部类(.*)。
注意:
1. 在源文件中使用import显式的导入指定包下的类或接口
2. 声明在包的声明和类的声明之间。
3. 如果需要导入多个类或接口,那么就并列显式多个import语句即可
4. 举例:可以使用java.util.*的方式,一次性导入util包下所有的类或接口。
5. 如果导入的类或接口是java.lang包下的,或者是当前包下的,则可以省略此import语句。
6. 如果在代码中使用不同包下的同名的类。那么就需要使用类的全类名的方式指明调用的是哪个类。
7. 如果已经导入java.a包下的类。那么如果需要使用a包的子包下的类的话,仍然需要导入。
8. import static组合的使用:调用指定类或接口下的静态的属性或方法


PackageImportTest:
package com.atguigu06.package_import;

import com.atguigu05.method_more._04recursion.RecursionTest;
import com.atguigu05.method_more._04recursion.exer2.RabbitExer;

import java.lang.reflect.Field;
import java.util.*;

import static java.lang.System.out;
import static java.lang.Math.PI;
/**
 * ClassName: PackageImportTest
 * Package: com.atguigu06.package_import
 * Description:
 *
 * @Author: shkstart
 * @Create 2025-03-19 21:28
 * @Version: 1.0
 */
public class PackageImportTest {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        List list = new ArrayList();
        HashMap map = new HashMap();
        HashSet set = new HashSet();
        String str = "hello";
        System.out.println(str);
        Person p = new Person();
        Field field = null;
        RecursionTest test = null;
        RabbitExer exer = null; //包下面还有子包
        //Date可以使用import的方式指明
        Date date = new Date();
        //使用全类名的方式
        java.sql.Date date1 = new java.sql.Date(121231231L);
        out.println("hello");
        out.println(PI);
    }
}

面向对象特征之一:封装和隐藏

1.为什么需要封装性?
理论上:
    -`高内聚`:类的内部数据操作细节自己完成,不允许外部干涉
    - `低耦合`:仅暴露少量的方法给外部使用,尽量方便外部调用。
通俗的说:把该隐藏的隐藏起来,该暴露的暴露出来。这就是封装性的设计思想
2.如何实现数据封装?
2.1 权限修饰符
Java规定了4种杈限修饰,分别是:private、缺省、protected、public

2.2 作用
我们可以使用4种权限修饰来修饰类及类的内部成员。当这些成员被调用时,体现可见性的大小。

2.3 实际案例:
在题目中,我们给Animal的对象的legs属性赋值。在实际的常识中,我们知道legs不能赋值为负数的。但是如果
直接调用属性legs,是不能加入判断逻辑的。那怎么办呢?
> 将legs属性私有化(private),禁止在Animal类的外部直接调用此属性
> 提供给legs属性赋值的setLegs()方法,在此方法中加入legs赋值的判断逻辑if(legs>=0 && legs % 2 ==0){}
 将此方法暴露出去,使得在Animal类的外部调用此方法,对legs属性赋值。
> 提供给legs属性获取的getLeqs()方法,此方法对外暴露。使得在Animal类的外部还可以调用此属性的值。

2.4 4种权限具体使用
《见课件》
> 类:只能使用public、缺省修饰
> 类的内部成员:可以使用4种权限修饰进行修饰。
2.5 开发中4种权限使用频率的情况:
    比较高:public、private
    比较低:缺省、protected

3.封装性的体现
> 场景1:私有化(private)类的属性,提供公共(public)的get和set方法,对此属性进行获取或修改
> 场景2:将类中不需要对外暴露的方法,设置为private.
> 场景3:单例模式中构造private的了,避免在类的外部创建实例。((放到static关键字后讲)

案例:

public class AnimalTest {
    public static void main(String[] args) {
        Animal animal1 = new Animal();
        animal1.name = "金蟾";
        //因为legs声明为private,是私有的,出了Animal类之外就不能调用了
//        animal1.legs = 4;
//        animal1.legs = -4;
        //只能通过setLegs(),间接的对legs属性进行赋值
        animal1.setLegs(2);
        animal1.setLegs(-2);
//        System.out.println("name = " + animal1.name + ", legs = " + animal1.legs);
        System.out.println("name = " + animal1.name + ", legs = " + animal1.getLegs());

        animal1.eat();
    }
}
class Animal {
    //属性
    String name; //名字
    private int legs; //腿的个数
    //方法
    //设置legs的属性值
    public void setLegs(int l){
        if(l >= 0 && l % 2 == 0){
            legs=l;
        }else {
            System.out.println("你输入的数据非法");
        }
    }
    //获取legs的属性值
    public int getLegs(){
        return legs;
    }

    //方法
    public void eat(){
        System.out.println("动物觅食");
    }
}
使用 com.atguigu07.encapsulation.exer4.test1和com.atguigu07.encapsulation.exer4.test2
两个包来测试几种常见的权限修饰符。
OrderTest
package com.atguigu07.encapsulation.exer4.test1;

/**
 * ClassName: OrderTest
 * Package: com.atguigu07.encapsulation.exer4.test1
 * Description:
 *
 * @Author: shkstart
 * @Create 2025-03-20 10:56
 * @Version: 1.0
 */
public class OrderTest {
    public static void main(String[] args) {
        Order order = new Order();
        //调用属性
        order.orderDefault = 1;
        order.orderPublic = 2;
        //调用方法
        order.methodDefault();
        order.methodPublic();

        //不能调用
//        order.orderPrivate = 3;
//        order.methodPrivate();
    }
}

Order 
package com.atguigu07.encapsulation.exer4.test1;

/**
 * ClassName: Order
 * Package: com.atguigu07.encapsulation.exer4.test1
 * Description:
 *
 * @Author: shkstart
 * @Create 2025-03-20 10:50
 * @Version: 1.0
 */
//class Order { //缺省在不同包的子类下面不能被调用
public class Order {
    //声明不同权限的属性
    private int orderPrivate;
    int orderDefault;
    public int orderPublic;
    //声明不同权限的方法
    private void methodPrivate(){

    }
    void methodDefault(){

    }
    public void methodPublic(){

    }
}

package com.atguigu07.encapsulation.exer4.test2;

import com.atguigu07.encapsulation.exer4.test1.Order;

/**
 * ClassName: OrderTest
 * Package: com.atguigu07.encapsulation.exer4.test2
 * Description:
 *
 * @Author: shkstart
 * @Create 2025-03-20 11:00
 * @Version: 1.0
 */
public class OrderTest {
    public static void main(String[] args) {
        Order order = new Order();
        //调用属性
        order.orderPublic = 1;
        //调用方法
        order.methodPublic();

        //不能调用
//        order.orderPrivate = 1;
//        order.orderDefault = 2;

//        order.methodPrivate();
//        order.methodDefault();
    }
}

类的成员之三:构造器(构造方法)

原本创建的文件

反编码之后生成的文件(out文件里面 )

如果在类文件中没有自定义创建构造器,则会自动生成一个空的构造器

类的成员之三:构造器
1.构造器的理解
constructor: n. 建设者、建造者
construct:v,建设、建造、创造
construction: n. 建设、建造 CCB

2.构造器的作用
作用1:搭配new关键字,创建类的对象
作用2:在创建对象的同时,可以给对象的相关属性赋值

3.构造器的使用说明
> 构造器声明的格式:权限修饰符 类名(形参列表){}
> 创建类以后,在没有显示提供任何构造器的情况下,系统会默认提供一个空参的构造器,且构造器的权限
    与类声明的权限相同。
> 一旦类中显示声明了构造器,则系统不再提供默认的空参的构造器
> 一个类中可以声明多个构造器,彼此之间构成重载。
PersonTest 
public class PersonTest {
    public static void main(String[] args) {
        Person p1 = new Person(); //>一旦类中显示声明了构造器,则系统不再提供默认的空参的构造器,所以这个地方没有空的相同person方法,会报错
        p1.eat();
        Person p2 = new Person(1);
        System.out.println(p2.age);  //1
        Scanner sc = new Scanner(System.in); //这也是一个构造器,传入了一个实参
    }
}

Person 
class Person {
    //属性
    String name;
    int age;

    //构造器  空参的构造器
    public Person(){
        System.out.println("Person()....");
    }
    //声明其他构造器
//    >一旦类中显示声明了构造器,则系统不再提供默认的空参的构造器
    public Person(int a){
        age = a;
    }
    public Person(String n){
        name = n;
    }
    public Person(String n,int a){
        name = n;
        age = a;
    }

    //方法
    public void eat(){
        System.out.println("人吃饭");
    }
    public void sleep(int hour){
        System.out.println("每天睡眠" + hour + "小时");
    }
}

语法格式

练习:

案例:

1、写一个名为Account的类模拟账户。该类的属性和方法如下图所示。
该类包括的属性:账号id,余额balance,年利率annualInterestRate;
包含的构造器:自定义
包含的方法:访问器方法(getter和setter方法),取款方法withdraw(),存款方法deposit()。

提示:在提款方法withdraw中,需要判断用户余额是否能够满足提款数额的要求,如果不能,应给出提示。

2、创建Customer类。
a.声明三个私有对象属性:firstName、lastName和account。
b.声明一个公有构造器,这个构造器带有两个代表对象属性的参数(f和L)
c.声明两个公有存取器来访问该对象属性,方法getFirstName和getLastName返回相应的属性。
d.声明setAccount方法来对account属性赋值。
e.声明getAccount方法以获取account属性。

3、写一个测试程序。
(1)创建一个Customer ,名字叫 Jane smith,他有一个账号为1000,余额为2000元,年利率为 1.23% 的账户
(2)对Jane Smith操作。
存入 100元,再取出960元。再取出2000元。
打印出Jane Smith 的基本信息:

成功存入 :100.0
成功取出:960.0
余额不足,取款失败

Customer [Smith, Jane] has a account: id is 1000,annualInterestRate is 1.23%, balance is 1140.0.


Account 
public class Account {
    private int id; //账号
    private double balance; //余额
    private double annualInterestRate; //年利率
//    public Account(){
//
//    }
    public Account(int i,double b,double a){
        id = i;
        balance = b;
        annualInterestRate = a;
    }
    public void setId(int i){
        id = i;
    }
    public int getId() {
        return id;
    }
    public void setBalance(double b){
        balance = b;
    }
    public double getBalance(){
        return balance;
    }
    public void setAnnualInterestRate(double a){
        annualInterestRate = a;
    }
    public double getAnnualInterestRate(){
        return annualInterestRate;
    }
    public void withdraw(double amount){
        if(amount <= balance && amount > 0){
            balance -= amount;
            System.out.println("成功取出" + amount);
        }else{
            System.out.println("余额不足,取款失败");
        }
    }
    //存款
    public void deposit(double amount){
        if(amount > 0){
            balance += amount;
            System.out.println("成功存入:" + amount);
        }
    }
}

Customer 
public class Customer {
    private String firstName;
    private String lastName;
    private Account account;
    public Customer(String f, String l) {
        firstName = f;
        lastName = l;
    }
    public String getFirstName() {
        return firstName;
    }
    public String getLastName() {
        return lastName;
    }
    public void setAccount(Account a){
        account = a;
        //这里的a可以多次调用,匿名对象传递过来的时候,这边就有名字了
    }
    public Account getAccount() {
        return account;
    }
}

CustomerTest 
package com.atguigu08.constructor.exer3;

/**
 * ClassName: CustomerTesr
 * Package: com.atguigu08.constructor.exer3
 * Description:
 *
 * @Author: shkstart
 * @Create 2025-03-20 13:47
 * @Version: 1.0
 */
public class CustomerTest {
    public static void main(String[] args) {
        //创建Customer实例
        Customer customer = new Customer("Jane","Smith");
//        Account account = new Account(1000,2000,0.0123);
//        customer.setAccount(account);
        //或 匿名对象的常见用法
        customer.setAccount(new Account(1000,2000,0.0123));

        //针对于客户的账户进行取钱,存钱的操作
//        account.deposit(100);
//        account.withdraw(960);
//        account.withdraw(2000);
        //针对于用户存钱与取钱的操作,最好不要是账户类去操作,相当于是银行去操作了,而是从客户当中获取到他自己的账户,再去操作存钱去取钱操作
        customer.getAccount().deposit(100);
        customer.getAccount().withdraw(960);
        customer.getAccount().withdraw(2000);

        //输出客户信息
        //Customer [Smith, Jane] has a account: id is 1000,
        // annualInterestRate is 1.23%, balance is 1140.0.
        System.out.println("Customer [" + customer.getFirstName() + "," + customer.getLastName() +"] has a account: id is " + customer.getAccount().getId() + ",annualInterestRate is " + customer.getAccount().getAnnualInterestRate() * 100 + ",balance is " + customer.getAccount().getBalance());
        /**
         * 关于匿名对象
         *
         * 匿名对象往往只能被调用一次
         * 匿名对象常常作为实参传递给方法的形参
         * 下面的两行创建的是两个对象
         * 引用大数据类型与普通数据类型一样,都是通过声明的变量来操作具体的值
         */
        new Account(1001,2000,0.0123).withdraw(1000); //成功取出1000.0
        System.out.println(new Account(1001,2000,0.0123).getBalance()); //2000.0
        int num = 10;
    }
}

属性赋值过程

JavaBean

在插件市场中下载jclasslib可以查看字节码文件  

 

UML类图

关键字:this

PersonTest  

public class PersonTest {
    public static void main(String[] args) {
        Person p1 = new Person();
        p1.setAge(10);
        System.out.println(p1.age);
        Person p2 = new Person("Tom","tom@126.com");
        System.out.println(p2.name+"\t"+p2.email);
    }
}

class Person{
    String name;
    int age;
    String email;
    public Person() {

    }
    public Person(String name,String email){
        this.name = name;
        this.email = email;
    }

    public void setAge(int age){
//        age = age; //就近原则,此时的age指向的都是形参
        this.age = age;
    }
    public int getAge(){
//        return age; //这个地方的this可以省略
//        或
        return this.age;
    }
    public void eat(){
        System.out.println("人吃饭");
        this.sleep();
    }
    public void sleep(){
        System.out.println("人睡觉");
    }

}

UserTest: 


1.目前可能出现的问题?及解决方案?
我们在声明一个属性对应的setxxx方法时,通过形参给对应的属性赋值。
如果形参名和属性名同名了,那么该如何在方法内区分这两个变量呢?

解决方案:使用this。具体来讲,使用this修饰的变量,表示的是属性。没有用this修饰的,表示的是形参。

2.this可以调用的结构:成员变量、方法、构造器

3.this的理解:当前对象(在方法中调用时) 或 当前正在创建的对象(在构造器中调用时)

4.1 this调用属性和方法
针对于方法内的使用情况:(准确的说是非static修饰的方法)
一般情况:我们通过对象a调用方法,可以在方法内调用当前对象a的属性或其他方法。此时,我们可以在属性和其他方法前
使用"this.",表示当前属性或方法所属的对象a。但是,一般情况下,我们都选择省略此"this."结构。
特殊情况:如果方法的形参与对象的属性同名了,我们必须使用"this."进行区分。使用this.修饰的变量即为属性(或成员变量)
没有使用this.修饰的变量,即为局部变量。

【针对于构造器内的使用情况:】
一般情况:我们通过构造器创建对象时,可以在构造器内调用当前正在创建的对象的属性或方法。此时,我们可以在属性和方法前
使用"this.",表示当前属性或方法所属的对象。但是,一般情况下,我们都选择省略此"this."结构。
特殊情况:如果方法的形参与对象的属性同名了,我们必须使用"this."进行区分。使用this.修饰的变量即为属性(或成员变量)
没有使用this.修饰的变量,即为局部变量。

4.2 this调用构造器
> 格式:"this(形参列表)"
> 我们可以在类的构造器中,调用当前类中指定的其它构造器
> 要求:"this(形参列表)"必须声明在当前构造器的首行
> 结论:"this(形参列表)"在构造器中最多声明一个
> 如果一个类中声明了n个构造器,则最多有n-1个构造器可以声明有"this(形参列表)"的结构

public class UserTest {
}
class User {
    String name;
    int age;
    //可以将多个相同的方法放在构造器中,或者单独声明一个方法的方式
    public User() {
//        this("Tom",12); //这样写会造成递归循环
        //模拟对象创建时,需要初始化50行代码
    }
    public User(String name){
        //模拟对象创建时,需要初始化50行代码
        this(); //用这个表达调用的是User()这个构造器
        this.name = name;
    }
    public User(String name,int age){
        //模拟对象创建时,需要初始化50行代码
//        this();
//        this.name = name;
        this(name);
        this.age = age;
    }
    private void init(){
        //模拟对象创建时,需要初始化50行代码
    }


}

alt+insert按键调用出getter和setter方法可以批量生成get和set方法

生成空参构造器(会根据选择的顺序来决定参数的顺序):

ctrl+选中的元素,跳转到元素的来源

练习:

内存解析


http://www.kler.cn/a/593887.html

相关文章:

  • 智能AI流式输出的前端展现过程
  • 实现一个函数,将驼峰命名法的字符串转换为下划线命名法。
  • 剑指 Offer II 112. 最长递增路径
  • Java的表达式自动类型提升
  • Java操作RabbitMQ
  • 基于ArcGIS和ETOPO-2022 DEM数据分层绘制全球海陆分布
  • VLLM专题(三十一)—架构概述
  • 蓝桥杯十四届C++B组真题题解
  • 计算机网络基础:网络配置与管理
  • springboot实现文件上传到服务器上,并通过url访问
  • 批量将 PPT 转换为PDF/XPS/JPG图片等其它格式
  • 谈谈 CSS 中z - index属性的作用及在什么情况下会失效。
  • LVGL和其他图形库区别于联系
  • 1.环境搭建VUE+Spring boot
  • 「清华大学、北京大学」DeepSeek 课件PPT专栏
  • 小型状态机实现
  • Kubeasz工具快速部署K8Sv1.27版本集群(二进制方式)
  • Promethues 添加访问密码
  • 数据结构与算法的学习路线
  • Redis设置开机自启报错start-limit-hit