【Java个人自学笔记·二】韩顺平零基础学Java(P192-288

类与对象

用单独的变量来解决,不利于数据的管理。

用数组来解决,只能通过下标来获取信息,会造成变量名字和内容的对应关系不明确。

用单独的变量和数组来解决,都不利于数据的管理,效率低,故引出类与对象。

类就是数据类型,对象就是一个具体的实例。

类含有属性和行为。

从类到对象的叫法:创建一个对象,实例化一个对象,把类实例化。。。

Java最大的特点就是面向对象。

/*
使用面向对象的方式来解决养猫问题
定义一个猫类 -> cat -> 自定义的数据类型
*/

class Cat{
    //属性
    String name;  //姓名
    int age;  //年龄
    String color;  //颜色

    //行为
}

public class Example {
    public static void main(String[] args) {
        //使用面向对象解决
        //实例化一只猫

        //new Cat()创建一只猫
        //Cat cat1 = new Cat() 把创建的猫赋给cat1
        //cat1就是一个对象,cat2也是一个对象
        Cat cat1 = new Cat();
        cat1.name = "小白";
        cat1.age = 3;
        cat1.color = "白色";

        Cat cat2 = new Cat();
        cat2.name = "小蓝";
        cat2.age = 4;
        cat2.color = "蓝色";

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

类与对象的区别与联系

  1. 类是抽象的,概念的,代表一类事物,是数据类型。
  2. 对象是具体的,实际的,代表一个具体事物,是实例。
  3. 类是对象的模板,对象是类的一个个体,对应于一个实例。

对象在内存中的存在形式 

属性和成员变量

成员变量 = 属性 = field,即成员变量是用来表示属性的。

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

属性的使用细节

  1. 属性的定义语法同变量。实例:访问修饰符 属性类型 属性名;
  2. 属性的定义类型可以为任意类型,包含基本类型或引用类型。
  3. 属性如果不赋值,有默认值,默认值的规则和数组一致。 

如何创建对象

先声明再创建

Cat cat;
cat = new Cat();

直接创建

Cat cat = new Cat();

如何访问属性

对象名.属性名;
cat.name;
cat.age;
cat.color;

类与对象的内存分配机制

Person p1 = new Person();
p1.age = 10;
p1.name = "小明";
Person p2 = p1;  //把p1赋给了p2
System.out.println(p2.age);

Java内存的结构分析

  1. 栈:一般存放基本数据类型(局部变量)。
  2. 堆:存放对象(Cat cat,数组等)。
  3. 方法区:常量池(常量),类加载信息。

Java创建对象的流程简单分析

Person p = new Person();
p.name = "jack";
p.age = 10;
  1. 先加载Person类信息(属性和方法信息,只会加载一次)。
  2. 在堆中分配空间,进行默认初始化。
  3. 把地址赋给p,p就指向对象。
  4. 进行指定初始化。比如p.name = "jack",p.age = 10。

成员方法

在某些情况下,我们需要定义成员方法(简称方法)。

方法是对类的完善。

为什么需要成员方法?

class MyTools{
    public void printArr(int[][] map){
        for(int i = 0;i < map.length;i++){
            for(int j = 0;j < map[i].length;j++){
                System.out.print(map[i][j] + " ");
            }
        }
        System.out.println();
    }
}

public class Example {
    public static void main(String[] args) {
        int[][] map = {{0,0,1},{1,1,1},{1,1,3}};
        //传统方式进行遍历,倘若多个数组,就需要更改,要一直增加,很不方便。
        System.out.println("----------------传统方法--------------------");
        for(int i = 0;i < map.length;i++){
            for(int j = 0;j < map[i].length;j++){
                System.out.print(map[i][j] + " ");
            }
        }
        System.out.println();
        System.out.println("----------------成员方法--------------------");
        //成员方法,把输出的功能写到一个类的方法中,然后调用该方法即可。
        MyTools myTools = new MyTools();
        myTools.printArr(map);
    }
}

成员方法较于传统方式能提高代码的复用性,可以将实现的细节封装起来,然后供其他用户调用。 

class Person{
    //添加speak成员方法,输出“我是一个好人”
    //public表示方法是公开的
    //void表示方法没有返回值
    //speak表示方法名
    //()表示形参列表
    //{}表示方法体,写我们将要执行的代码
    //System.out.println("我是一个好人");表示这个方法就是输出“我是一个好人”
    public void speak(){
        System.out.println("我是一个好人");
    }


}

public class Example {
    public static void main(String[] args) {
        //方法使用
        //方法写好后,如果不去调用,就不会输出。
        //先创建对象,然后调用方法。
        Person person = new Person();
        person.speak();  //调用方法
    }
}
import java.util.Scanner;

class Person{

    //添加cal01成员方法,可以计算1+.....+1000的计算结果
    public void cal01(){
        int res = 0;
        for(int i = 1;i <= 1000;i++){
            res += i;
        }
        System.out.println("cal01:sum = " + res);
    }

    //添加cal02成员方法,该方法可以接收一个数,计算1+.....+n的计算结果
    public void cal02(int n){
        int res = 0;
        for(int i = 1;i <= n;i++){
            res += i;
        }
        System.out.println("cal02:sum = " + res);
    }

    //添加getSum成员方法,可以计算两个数的和。
    public int getSum(int n,int m){
        int res = n + m;
        return res;
    }
}

public class Example {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int m = scanner.nextInt();
        Person person = new Person();
        person.cal01();  //调用cat01方法
        person.cal02(n);  //调用cat02方法
        System.out.println("getSum:sum = " + person.getSum(n,m));  //调用getSum方法并输出结果
    }
}

方法的调用

  1. 当程序执行到方法时,就会开辟一个独立的空间(栈空间)。
  2. 当方法执行完毕,或者执行到return语句时,就会返回。
  3. 返回到调用方法的地方。
  4. 返回后,继续执行方法后的代码。
  5. 当主方法(栈)执行完毕,整个程序退出。

成员方法的定义

public 返回数据类型 方法名 (形参列表){
    语句;
    return 返回值;
}
  1.  形参列表:表示成员方法输入。
  2. 返回数据类型:表示成员方法输出,void表示没有返回值。
  3. 方法主体:表示为了实现某一功能代码块。
  4. return语句不是必须的。

成员方法的使用细节

  1. 访问修饰符用来控制方法使用的范围,如果不写默认访问。
  2. 一个方法最多有一个返回值。要实现一个方法可以返回多种结果,可以使用数组。
    class Arr{
        public int[] getSumAndSub(int n1,int n2){
            int[] resArr = new int[2];
            resArr[0] = n1 + n2;
            resArr[1] = n1 - n2;
            return resArr;
        }
    }
    
    public class Example {
        public static void main(String[] args) {
            Arr arr = new Arr();
            int[] res = arr.getSumAndSub(1,4);
            System.out.println("sum = " + res[0]);
            System.out.println("sub = " + res[1]);
        }
    }
  3. 如果方法要求有数据类型,则方法体中最后的执行语句必须为return值,而且要求返回值类型必须是和return的值类型一致或兼容,即可以发生自动类型转换的。
  4. 如果方法是void,则方法体中看有没有return语句,或者只写return。

形参列表的使用细节

  1. 一个方法可以有0个参数,也可以有多个参数,中间用逗号隔开。
  2. 参数类型可以为任意类型,包含基本类型或引用类型。
  3. 调用带参数的方法时,一定对应着参数列表传入相同类型或兼容类型的参数。
  4. 方法定义时的参数称为形式参数,简称形参。方法调用时的参数称为实际参数,简称实参。实参和形参的类型要一致或兼容、个数、顺序必须一致! 
  5. 方法体里面完成功能的具体语句,可以为输入、输出、变量、运算、分支、循环、方法调用,但里面不能再定义方法,即方法不能再嵌套定义。

方法调用的使用细节

  1. 同一个类中的方法调用:直接调用即可。
    class A{
        public void print(int n){
            System.out.println("print()方法被调用,n = " + n);
        }
        public void sayOK(){
            print(10);
            System.out.println("继续执行sayOK。。。");
        }
    }
    
    public class Example {
        public static void main(String[] args) {
            A a = new A();
            a.sayOK();
        }
    }
  2. 跨类中的方法A类调用B类:需要通过对象名调用。
    class A{
        public void m(){
            //创建B类对象,然后再调用方法。
            System.out.println("A类的m()方法被调用");
            B b = new B();
            b.Hi();
            System.out.println("A类继续执行。。。");
        }
    }
    
    class B{
        public void Hi(){
            System.out.println("B类的Hi()方法被调用");
        }
    }
    
    public class Example {
        public static void main(String[] args) {
            A a = new A();
            a.m();
        }
    }
  3. 特别注意:跨类的方法调用和方法的访问修饰符有关。 

例题

/*
编写类AA,有一个方法:判断一个数是奇数odd还是偶数,返回Boolean
*/

public class Example {
    public static void main(String[] args) {
        AA aa = new AA();
        if(aa.isOdd(1)){
            System.out.println("是一个奇数");
        } else {
            System.out.println("是偶数");
        }
    }
}

class AA {
    /*
    方法的返回类型 Boolean
    方法的名字 isOdd
    方法的形参 (int num)
    方法体 判断
     */
    public boolean isOdd(int num){
//        if (num % 2 != 0){
//            return true;
//        } else {
//            return false;
//        }
        return num % 2 != 0;
    }
}
/*
根据行、列、字符打印 对应行数和列数的字符,比如:行:4,列:4,字符#,则打印相应效果
####
####
####
####
*/

public class Example {
    public static void main(String[] args) {
        AA aa = new AA();
        int row = 4;
        int col = 4;
        char c = '#';
        aa.print(row,col,c);
    }
}

class AA {
    /*
    方法的返回类型 void
    方法的名字 print
    方法的形参 (int row,int col,char c)
    方法体 循环
     */
    public void print(int row,int col,char c){
        for(int i = 0;i < row;i++){
            for(int j = 0;j < col;j++){
                System.out.print(c);
            }
            System.out.println();
        }
    }
}

方法传参机制

/*
输出:
交换前:a = 10 b = 20
交换后:a = 20 b = 10
---------------------
主方法:a = 10 b = 20
为什么交换后调回原函数还是10和20?
是独立的数据空间,互不影响
*/

public class Example {
    public static void main(String[] args) {
        int a = 10;
        int b = 20;
        AA aa = new AA();
        aa.swap(a,b);  //调用swap方法
        System.out.println("---------------------");
        System.out.println("主方法:a = " + a + " b = " + b);
    }
}

class AA {
    public void swap(int a,int b){
        System.out.println("交换前:a = " + a + " b = " + b);
        //交换
        int temp = a;
        a = b;
        b = temp;
        System.out.println("交换后:a = " + a + " b = " + b);
    }
}
/*
输出:
test100的arr输出:
200	2	3
main的arr输出:
200	2	3

为什么这里的又有影响了?
数组引用的是空间,传递的是地址

只要是调用方法就会产生一个新栈
*/

public class Example {
    public static void main(String[] args) {
        AA aa = new AA();
        int[] arr = {1,2,3};
        aa.test100(arr);
        System.out.println("main的arr输出:");
        for (int i = 0;i < arr.length;i++){
            System.out.print(arr[i] + "\t");
        }
        System.out.println();
    }
}

class AA {
    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();
    }
}

引用数据类型(数组,对象)传递的都是地址,传参会对主函数有所影响。

/*
输出:
main的person.age = 10000

引用类型(数组,对象)都是地址引用,都会有影响
*/

public class Example {
    public static void main(String[] args) {
        Person person = new Person();
        person.name = "xiaoyan";
        person.age = 21;
        AA aa = new AA();
        aa.test200(person);
        System.out.println("main的person.age = " + person.age);
    }
}

class Person {
    String name;
    int age;
}

class AA {
    public void test200(Person p){
        p.age = 10000;  //修改对象属性
    }
}
/*
输出:
main的person.age = 21

为什么是引用数据类型 却没有改变?
因为person = null,person指向的是一个空地址,并不是person的。
*/

public class Example {
    public static void main(String[] args) {
        Person person = new Person();
        person.name = "xiaoyan";
        person.age = 21;
        AA aa = new AA();
        aa.test200(person);
        System.out.println("main的person.age = " + person.age);
    }
}

class Person {
    String name;
    int age;
}

class AA {
    public void test200(Person person){
        person = null;  //修改对象属性
    }
}
/*
输出:
main的person.age = 21

为什么是引用数据类型 却没有改变?
因为person = new Person(),person创建了一个新的对象。
person在哪里调用,就看哪里的地址。
*/

public class Example {
    public static void main(String[] args) {
        Person person = new Person();
        person.name = "xiaoyan";
        person.age = 21;
        AA aa = new AA();
        aa.test200(person);
        System.out.println("main的person.age = " + person.age);
    }
}

class Person {
    String name;
    int age;
}

class AA {
    public void test200(Person person){
        person = new Person();
        person.age = 30;
        person.name = "fight";
    }
}
/*
编写一个方法copyPerson,可以复制一个Person对象,返回复制的对象。克隆对象,
注意要求得到新对象和原来的对象是两个独立的对象,只是它们是属性相同
*/

public class Example {
    public static void main(String[] args) {
        Person person = new Person();
        person.name = "xiaoyan";
        person.age = 21;

        MyTools myTools = new MyTools();
        Person person1 = myTools.copyPerson(person);
        //person和person1是两个独立的对象,只是都是Person对象,属性相同
        System.out.println("person的属性:");
        System.out.println("name = " + person.name + " age = " + person.age);
        System.out.println("-----------------------");
        System.out.println("person1的属性:");
        System.out.println("name = " + person1.name + " age = " + person1.age);
        //可以通过看看两个对象是否是同一个对象
        System.out.println("-----------------------");
        System.out.println("person与person1是否是一个对象?");
        System.out.println(person == person1);

    }
}

class Person {
    String name;
    int age;
}

class MyTools {
    /*
    方法的返回类型 Person
    方法的名字 copyPerson
    方法的形参 Person person
    方法体 创建一个新对象 并复制属性 返回即可
     */
    public Person copyPerson(Person person){
        Person person1 = new Person();
        person1.name = person.name;  //把原来的赋值到新的
        person1.age = person.age;
        return person1;
    }
}

方法递归调用

递归就是方法自己调用自己,每次调用时传入不同的遍历,递归有助于编程者解决复杂问题,同时可以让代码变得简洁。

/*
打印问题

输出:
n = 2
n = 3
n = 4
*/

public class Example {
    public static void main(String[] args) {
        T t = new T();
        t.test(4);
    }
}

class T {
    public void test(int n) {
        if(n > 2){
            test(n - 1);  //在这里发生的递归
        }
        System.out.println("n = " + n);  //test(3) test(2) -> 2 -> 3 -> 4 栈,最后进去的先输出
    }
}
/*
阶乘

输出:
120

5 * 4 * 3 * 2 * 1 = 120
*/

public class Example {
    public static void main(String[] args) {
        T t = new T();
        int res = t.factorial(5);
        System.out.println(res);
    }
}

class T {
    public int factorial(int n) {
        if (n == 1){
            return 1;
        } else {
            return factorial(n - 1) * n;
        }
    }
}

递归调用的使用细节

  1. 执行一个方法时,就创建一个新的受保护的独立空间(栈空间)。
  2. 方法的局部变量是独立的,不会相互影响。
  3. 如果方法中使用的是引用类型变量(比如数组),就会共享该引用类型的数据,因为引用类型指向的是地址。
  4. 递归必须向退出递归的条件逼近,否则就是无限递归,出现(死龟)。
  5. 当一个方法执行完毕,或者遇到return,就会返回,遵守谁调用,谁将结果返回给谁,同时当方法执行完毕或者返回时,该方法也就执行完毕。 

例题

/*
请使用递归的方式求出斐波那契数1,1,2,3,5,8,13...给你一个整数n,求出它的值是多少
斐波那契数 第三个数开始,和是前两个数的和
*/

public class Example {
    public static void main(String[] args) {
        T t = new T();
        int n = 7;
        int res = t.Fibonacci(n);
        if (res != -1) {
            System.out.println("当n = " + n +"时,对应的斐波那契数为:" + res);
        }
    }
}

class T {
    /*
    当n = 1,斐波那契数是1
    当n = 2,斐波那契数是1
    当n >= 3,斐波那契数是前两个数的和
     */
    public int Fibonacci(int n) {
        if (n >= 1) {
            if (n == 1 || n == 2) {
                return 1;
            } else {
                return Fibonacci(n - 1) + Fibonacci(n - 2);
            }
        } else {
            System.out.println("要求输入的数是大于等于1的数");
            return -1;
        }
    }
}
/*
猴子吃桃子问题:
有一堆桃子,猴子第一天吃了其中的一般,并再多吃了一个
以后每天桃子都吃其中的一般,然后再多吃一个。
当到第10天时,想再吃时(即还没吃),发现只有一个桃子了。
问:最初共有多少个桃子?
*/

public class Example {
    public static void main(String[] args) {
        T t = new T();
        int day = 1;
        int res = t.peach(day);
        if (res != -1) {
            System.out.println("第" + day + "天共有" + res + "个桃");
        }
    }
}

class T {
    /*
    day = 10时有n = 1
    day = 9时有(day10 + 1) * 2 = 4
    day = 8时有(day9 + 1) * 2 = 10
    规律:前一天的桃子 = (后一天的桃子 + 1) * 2
     */
    public int peach (int day) {
        if(day == 10){
            return 1;
        } else if (day >= 1 && day <= 9){
            return (peach(day + 1) + 1) * 2;
        } else {
            System.out.println("day要求在1-10之内");
            return -1;
        }
    }
}
/*
迷宫问题

先设置一个二维数组表示迷宫 八行七列
先规定map数组的元素值:0:表示可以走,1:表示不可以走
*/

public class Example {
    public static void main(String[] args) {
        int[][] map = new int[8][7];
        for (int i = 0;i < 7;i++){
            map[0][i] = 1;
            map[7][i] = 1;
        }
        //将最右边和最左边的一列设置为1,不可走。
        for (int i = 0;i < 8;i++){
            map[i][0] = 1;
            map[i][6] = 1;
        }
        //将第四行的23设置为1 -> 下标0开始所以是1 2
        map[3][1] = 1;
        map[3][2] = 1;

        System.out.println("输出当前地图:");
        for(int i = 0;i < map.length;i++){
            for(int j = 0;j < map[i].length;j++){
                System.out.print(map[i][j] + " ");
            }
            System.out.println();
        }

        System.out.println("--------------------");
        T t = new T();
        t.FindWay(map,1,1);
        System.out.println("找路的情况如下:");
        for(int i = 0;i < map.length;i++){
            for(int j = 0;j < map[i].length;j++){
                System.out.print(map[i][j] + " ");
            }
            System.out.println();
        }
    }
}

class T {
    /*
    使用递归回溯的思想来解决出迷宫

    FindWay方法就是专门用来找出迷宫的路径
    如果找到,就返回ture,否则返回false。
    map就是二维数组,即表示迷宫
    i,j表示所在位置,初始化位置为(1,1)
    因为是可以递归的找路,所以规定map的各数值的含义
    0:表示可以走
    1:表示不可以走
    2:可以走
    3:走过,但是走不通,是死路
    当map[6][5] = 2的时候就说明找到通路,就可以结束,否则继续找
    先确定找路策略 下 -> 右 -> 上 -> 左
     */
    public boolean FindWay(int[][] map,int i,int j){
        if (map[6][5] == 2){  //已经找到
            return true;
        } else {
            if (map[i][j] == 0){  //表示可以走
                //假定可以走通
                map[i][j] = 2;
                //使用找路策略开始测试 下 -> 右 -> 上 -> 左
                if (FindWay(map,i + 1,j)){  //先走下
                    return true;
                } else if (FindWay(map,i,j + 1)){  //右
                    return true;
                } else if (FindWay(map,i - 1,j)){  //上
                    return true;
                } else if (FindWay(map,i,j - 1)){  //左
                    return true;
                } else {  //都走不通,则是死路
                    map[i][j] = 3;  //3:走过,但是走不通,是死路
                    return false;
                }
            } else {
                /*
                    除了0还有 1,2,3 已经测试过 就没有必要再走了
                    1:表示不可以走
                    2:可以走
                    3:走过,但是走不通,是死路
                 */
                return false;
            }
        }
    }
}
/*
汉诺塔

汉诺塔问题源于印度一个古老传说的益智玩具。
大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序放着64片圆盘。
大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。
并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。

这里弄5个简单说明,可以换成64,但运行时间很长
*/

public class Example {
    public static void main(String[] args) {
        T t = new T();
        t.move(5,'A','B','C');
    }
}

class T {
    /*
    number表示要移动的个数,abc分别表示a塔b塔c塔
     */
    public void move(int number,char a,char b,char c){
        if (number == 1) {
            System.out.println(a + " -> " + c);
        } else {
            //如果有多个盘,可以看成两个,最下面的和上面的所有盘(number-1)
            //先移动上面所有的盘到b,借助c
            move(number - 1,a,c,b);
            System.out.println(a + " -> " + c);
            //再把b塔的所有盘移动到c,借助a
            move(number - 1,b,a,c);
        }
    }
}

方法重载

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

/*
输出:
3
*/

public class Example {
    public static void main(String[] args) {
        MyCalculator myCalculator = new MyCalculator();
        System.out.println(myCalculator.calculate(1,2));
    }
}

class MyCalculator {
    //下面四个calculate方法重载
    public int calculate (int n1,int n2){  //调用了这个方法
        return n1 + n2;
    }
    public double calculate (int n1,double n2){
        return n1 + n2;
    }
    public double calculate (double n1,int n2){
        return n1 + n2;
    }
    public double calculate (int n1,int n2,int n3){
        return n1 + n2 + n3;
    }
}

方法重载的使用细节

  1. 方法名必须一样
  2. 形参列表必须不同,形参类型或个数或顺序必须不一样,形参的名字无所谓。
  3. 返回类型没有要求

例题

/*
编写程序,类methods中定义三个重载方法并调用。
方法名为m。
三个方法分别接受一个int参数、两个int参数、一个字符串参数。
分别执行平方运算并输出结果,相乘并输出结果,输出字符串信息。
在主类的main()方法中分别用参数区别调用三个方法。

输出:
1
20
你好
*/

public class Example {
    public static void main(String[] args) {
        Methods methods = new Methods();
        methods.m(1);
        methods.m(2,10);
        methods.m("你好");
    }
}

class Methods {
    public void m (int n){
        System.out.println(n * n);
    }
    public void m (int n1,int n2){
        System.out.println(n1 * n2);
    }
    public void m (String n){
        System.out.println(n);
    }
}
/*
编写程序,类methods中定义三个重载方法并调用。
方法名为max。
第一个方法:返回两个int值中的最大值。
第二个方法:返回两个double值中的最大值。
第三个方法:返回三个double值中的最大值。

输出:
1
20
你好
*/

public class Example {
    public static void main(String[] args) {
        Methods methods = new Methods();
        int a = methods.max(1,2);
        double b = methods.max(1.1,2.2);
        double c = methods.max(1.123,2.234,1.111);
        System.out.println("两个int值中的最大值为:" + a);
        System.out.println("两个double值中的最大值为:" + b);
        System.out.println("三个double值中的最大值为:" + c);
    }
}

class Methods {
    public int max (int n1,int n2){
        if (n1 >= n2){
            return n1;
        } else {
            return n2;
        }
    }

    public double max (double n1,double n2){
        if (n1 >= n2){
            return n1;
        } else {
            return n2;
        }
    }

    public double max (double n1,double n2,double n3){
        if (n1 >= n2){
            return n1;
        } else {
            if (n3 >= n2){
                return n3;
            } else {
                return n2;
            }
        }
    }
}

可变参数

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

public class Example {
    public static void main(String[] args) {
        HspMethod hspMethod = new HspMethod();
        int b = hspMethod.sum(1,5,100);
        System.out.println(b);
    }
}

class HspMethod {
    //方法重载
    public int sum (int n1,int n2){
        return n1 + n2;
    }
    public int sum (int n1,int n2,int n3){
        return n1 + n2 + n3;
    }
    public int sum (int n1,int n2,int n3,int n4){
        return n1 + n2 + n3 + n4;
    }

    //可变参数
    //int...表示可以接收多个int型的参数
    //使用可变参数时,可以当作数组来使用 即nums可以当作数组
    //遍历求和即可
    public int sum (int... nums){
        int res = 0;
        for(int i = 0;i < nums.length;i++){
            res += nums[i];
        }
        return res;
    }
}

可变参数的使用细节

  1. 可变参数的实参可以为0个或任意多个。
  2. 可变参数的实参可以为数组。
    public class Example {
        public static void main(String[] args) {
            int[] arr = {1,2,3};
            T t = new T();
            t.f(arr);
        }
    }
    
    class T {
        public void f(int... nums){
            System.out.println("长度 = " + nums.length);
        }
    }
    
  3. 可变参数的本质就是数组。
  4. 可变参数可以和普通类型的参数一起放在形参列表,但必须保证可变参数在最后
  5. 一个形参列表中只能出现一个可变参数。
/*
有三个方法,分别实现返回姓名和两门课成绩(总分),返回姓名和三门课成绩(总分),返回姓名和五门课成绩(总分)。
封装成一个可变参数的方法。
*/

public class Example {
    public static void main(String[] args) {
        HspMethod hspMethod = new HspMethod();
        String b = hspMethod.showScore("小焰",90.1,90.0);
        System.out.println(b);
    }
}

class HspMethod {
    public String showScore(String name , double... score){
        double totalScore = 0;
        for(int i = 0;i < score.length;i++){
            totalScore += score[i];
        }
        return name + score.length + "门课的成绩总分为 = " + totalScore;
    }
}

作用域

  1. 在Java编程中,主要的变量就是属性(成员变量)和局部变量。
  2. 局部变量一般是指在成员方法中定义的变量。
  3. Java中作用域的分类
    1. 全局变量:也就是属性,作用域为整个类体。
    2. 局部变量:也就是除了属性之外的其他变量,作用域为定义它的代码块中。
  4. 全局变量可以不赋值,直接使用,因为有默认值,局部变量必须赋值后,才能使用,因为没有默认值。

 作用域的使用细节

  1. 属性和局部变量可以重名,访问时遵循就近原则。
  2. 在同一个作用域中,比如在同一个成员方法中,两个局部变量,不能重名。
  3. 属性生命周期较长,伴随着对象的创建而创建,伴随着对象的死亡而死亡。
  4. 局部变量,生命周期较短,伴随着它的代码块的执行而创建,伴随着代码块的结束而死亡,即在一次方法调用过程中。
  5. 作用域范围不同
    1. 全局变量:可以在本类中使用,或其他类使用(通过对象调用)。
    2. 局部变量:只能在本类中对应的方法中使用。
  6. 修饰符不同
    1. 全局变量可以加修饰符。
    2. 局部变量不可以加修饰符。

构造器

[修饰符] 方法名(形参列表) {
    方法体;
}
  1. 构造器的修饰符可以默认。
  2. 构造器没有返回值。
  3. 方法名和类名字必须一样。
  4. 参数列表和成员方法一样的规则。
  5. 构造器的调用系统完成 。

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

  1. 方法名和类名相同。
  2. 没有返回值。
  3. 在创建对象时,系统会自动的调用该类的构造器完成对对象的初始化。
/*
输出:
构造器被调用,完成对象属性的初始化
name = xiaoyan age = 21
*/

public class Example {
    public static void main(String[] args) {
        Person person = new Person("xiaoyan",21);
        System.out.println("name = " + person.name + " age = " + person.age);
    }
}

class Person {
    String name;
    int age;
    public Person(String Name,int Age){
        System.out.println("构造器被调用,完成对象属性的初始化");
        name = Name;
        age = Age;
    }
}

构造器的使用细节

  1. 一个类可以定义多个不同的构造器,即构造器重载。
  2. 构造器名和类名要相同。
  3. 构造器没有返回值。
  4. 构造器是完成对象的初始化,并不是创建对象。
  5. 在创建对象时,系统自动的调用该类的构造方法。
  6. 如果程序员没有定义构造器,系统会自动给类生成一个默认无参构造器(也叫默认构造器)。可以使用Javap对照代码和字节码,从而了解编译器内部的工作。
  7. 一旦定义了自己的构造器,默认的构造器就被覆盖了,就不能再使用默认的无参构造器,除非再显式的定义一下。
/*
第一个无参构造器:
利用构造器设置所有人的age属性初始值都为18。
第二个构造器:
带pName和pAge两个参数的构造器,使得每次创建Person对象的同时初始化对象的age属性值和name属性值。
分别使用不同的构造器,创建对象。

输出:
name = null age = 18
name = xiaoyan age = 21
*/

public class Example {
    public static void main(String[] args) {
        Person person = new Person();
        System.out.println("name = " + person.name + " age = " + person.age);
        Person person1 = new Person("xiaoyan",21);
        System.out.println("name = " + person1.name + " age = " + person1.age);
    }
}

class Person {
    String name;
    int age;
    public Person(){
        age = 18;
    }
    public Person(String pName,int pAge){
        name = pName;
        age = pAge;
    }
}

对象创建的流程分析

class Person {
    int age = 90;
    System name;
    Person(String n,int a){
        name = n;
        age = a;
    }
}

Person p = new Person("小焰",21);
  1. 加载Person类信息,只会加载一次。
  2. 在堆中分配空间(地址)。
  3. 完成对象初始化
    1. 默认初始化age = 0 name = null
    2. 显式初始化age = 90 name = null
    3. 构造器初始化age = 21 name = 小焰
  4. 把对象在堆中的地址返回给person(person是对象名,可以理解成对象的引用)。

this关键字

Java虚拟机会给每个对象分配this,代表当前对象。- > 人描述自己的眼睛:我的眼睛(根据不同的人来说,“我的”指向也就不同)。

public class Example {
    public static void main(String[] args) {
        Dog dog1 = new Dog("小白",3);
        dog1.info();
    }
}

class Dog {
    String name;
    int age;
    /*
    public Dog(String dName,int dAge){  //构造器
        name = dName;
        age = dAge;
    }
    如果我们构造器的形参,能够直接写成属性名,就更好了
    但是出现了一个问题,根据变量的作用域原则
    name值为null?

    构造器的name就是局部变量而不是属性
    构造器的age就是局部变量而不是属性

    引入this关键字来解决
     */
    public Dog(String Name,int Age) {
        this.name = Name;  //this.name就是当前对象的属性name
        this.age = Age;  //this.age就是当前对象的属性age
        //谁在调用这个构造器,那么this就是谁的对象
    }
    public void info(){  //成员方法,输出对象的属性信息
        System.out.println(name + "\t" + age + "\t");
    }
}

this关键字的使用细节

  1. this关键字可以用来访问本类的属性、方法、构造器。
  2. this用于区分当前类的属性和局部变量。
  3. 访问成员方法的语法:this.方法名(参数列表);
    /*
    输出:
    f2方法...
    f1方法...
    f1方法...
    */
    
    public class Example {
        public static void main(String[] args) {
            T t = new T();
            t.f2();
        }
    }
    
    class T {
        public void f1(){
            System.out.println("f1方法...");
        }
        public void f2(){
            System.out.println("f2方法...");
            //调用本类的f1
            //第一种方式
            f1();
            //第二种方式
            this.f1();
        }
    }
  4. 注意访问构造器语法:this(参数列表);注意只能在构造器中使用。即只能在构造器中访问另一个构造器,this这个语句必须放在第一条语句
    /*
    输出:
    T(String name,int age)构造器...
    T()构造器...
    */
    
    public class Example {
        public static void main(String[] args) {
            T t = new T();
        }
    }
    
    class T {
        public T(){
            //这里去访问T(String name,int age)构造器
            //如果要用访问构造器语法,this(参数列表),必须放在第一条语句
            this("xiaoyan",21);
    
            System.out.println("T()构造器...");
    
    
        }
        public T(String name,int age){
            System.out.println("T(String name,int age)构造器...");
        }
    }
  5. this不能在类定义的外部使用,只能在类定义的方法中使用。
class T {
    public void f3(){
        String name = "xiaoyan";
        //传统方式
        System.out.println("name = " + name + " num = " + num);  //xiaoyan 100
        //也可以使用this访问属性,要更加的好,并不遵循就近原则,指定对象
        System.out.println("name = " + this.name + " num = " + this.num);  //fire 100
    }
}

例题

/*
定义Person类,里面有name,age属性,并提供compareTo比较方法,用于判断是否和另一个人相等
提供测试类TestPerson用于测试,名字和年龄完全一样,就返回true,否则返回false。
*/

public class Example {
    public static void main(String[] args) {
        Person person1 = new Person("xiaoyan",21);
        Person person2 = new Person("fire",18);
        System.out.println(person1.compareTo(person2));
    }
}

class Person{
    String name;
    int age;
    //构造器
    public Person(String name,int age){
        this.name = name;
        this.age = age;
    }
    public boolean compareTo(Person p){
//        if (this.name.equals(p.name) && this.age == p.age){
//            return true;
//        } else {
//            return false;
//        }
        return this.name.equals(p.name) && this.age == p.age;
    }
}
/*
编写类A01,定义方法max,实现求某个double数组的最大值,并返回
*/

public class Example {
    public static void main(String[] args) {
        double[] arr = {1.1,2.2,1.0};
        A01 a01 = new A01();
        Double res = a01.max(arr);
        if (res != null) {
            System.out.println("最大值为:" + res);
        } else {
            System.out.println("arr的输入有误,数组不能为空,或者{}");
        }
    }
}

class A01 {
    public Double max(double[] arr) {
        //保证arr至少有一个元素
        if (arr != null && arr.length > 0) {
            double maxNumber = arr[0];
            for (int i = 0; i < arr.length; i++) {
                if (maxNumber < arr[i]) {
                    maxNumber = arr[i];
                }
            }
            return maxNumber;
        } else {
            return null;
        }
    }
}
/*
编写类A02,定义方法find,实现查找某字符串是否在字符串数组中,并返回索引,如果找不到,返回-1。
*/

public class Example {
    public static void main(String[] args) {
        String[] strs = {"xiaoyan","fire","yan"};
        A02 a02 = new A02();
        int index = a02.find("yan",strs);
        System.out.println("查找的索引为:" + index);

    }
}

class A02 {
    public int find(String findStr,String[] strs){
        for (int i = 0;i < strs.length;i++){
            if(findStr.equals(strs[i])){
                return i;
            }
        }
        return -1;
    }
}
/*
编写类Book,定义方法updatePrice,实现更改某本书的价格,
具体:如果价格>150,则更改为150。如果价格>100,则更改为100,否则不变

输出
书名 = 笑傲江湖 价格 = 300.0
书名 = 笑傲江湖 价格 = 150.0
*/

public class Example {
    public static void main(String[] args) {
        Book book = new Book("笑傲江湖",300);
        book.info();
        book.updatePrice();
        book.info();
    }
}

class Book {
    String name;
    double price;
    public Book(String name,double price){
        this.name = name;
        this.price = price;
    }
    public void updatePrice(){
        //如果方法中,没有price局部变量,this.price 等价 price
        if (this.price > 150){
            this.price = 150;
        } else if (this.price > 100){
            this.price = 100;
        }
    }
    //显示书籍情况
    public void info(){
        System.out.println("书名 = " + this.name + " 价格 = " + this.price);
    }
}
/*
编写类A03,实现数组的复制功能copyArr,输入旧数组,返回一个新数组,元素和旧数组一样

输出
------返回的元素情况-------
10 30 50
*/

public class Example {
    public static void main(String[] args) {
        int[] oldArr = {10,30,50};
        A03 a03 = new A03();
        int[] newArr = a03.copyArr(oldArr);
        System.out.println("------返回的元素情况-------");
        for (int i = 0;i < newArr.length;i++){
            System.out.print(newArr[i] + " ");
        }
        System.out.println();
    }
}

class A03{
    public int[] copyArr(int[] oldArr){
        int[] newArr = new int[oldArr.length];  //创建了一个长度为oldArr.length的数组,即大小一样
        for (int i = 0;i < oldArr.length;i++){
            newArr[i] = oldArr[i];  //克隆
        }
        return newArr;
    }
}
/*
定义一个圆类Circle,定义属性:半径,提供显示圆周长功能的方法,提供显示圆面积的方法

输出
面积为:28.274333882308138 周长为:18.84955592153876
*/

public class Example {
    public static void main(String[] args) {
        Circle circle = new Circle(3);
        System.out.println("面积为:" + circle.area() + " 周长为:" + circle.Perimeter());
    }
}

class Circle{
    double radius;  //半径
    public Circle(double radius){
        this.radius = radius;
    }
    public double area(){  //面积
        return Math.PI * radius * radius;
    }
    public double Perimeter(){  //周长
        return 2 * Math.PI * radius;
    }
}
/*
编程创建一个Cale计算类,在其中定义2个变量表示两个操作数,
定义四个方法实现求和,差,乘,商(要求除数为0的话,要提示)
并创建两个对象,分别测试。

输出
和 = 12.0 减 = -8.0 乘 = 20.0 除 = 0.2
*/

public class Example {
    public static void main(String[] args) {
        Cale cale = new Cale(2, 10);
        System.out.print("和 = " + cale.sum() + " 减 = " + cale.minus() + " 乘 = " + cale.mul());
        Double divRes = cale.div();
        if (divRes != null) {
            System.out.println(" 除 = " + cale.div());
        }
    }
}

class Cale {
    double number1;
    double number2;
    public Cale(double number1,double number2){
        this.number1 = number1;
        this.number2 = number2;
    }
    //加
    public double sum(){
        return number1 + number2;
    }
    //减
    public double minus(){
        return number1 - number2;
    }
    //乘
    public double mul(){
        return number1 * number2;
    }
    //除
    public Double div(){
        if (number2 == 0){
            System.out.println("number2不能为0");
            return null;
        } else {
            return number1 / number2;
        }
    }
}
/*
输出
count1 = 10
count1 = 9
count1 = 10
*/

public class Example {
    int count = 9;
    public void count1(){
        count = 10;
        System.out.println("count1 = " + count);
    }
    public void count2(){
        System.out.println("count1 = " + count++);  //先输出再自增
    }
    public static void main(String[] args) {
        //new Example()是匿名对象,使用一次后就不能使用,被销毁
        //new Example().count1();创建好匿名对象后,就调用count1。
        new Example().count1();
        //创建对象,产生新对象,有堆,没有被销毁
        Example example = new Example();
        example.count2();
        example.count2();
    }
}
/*
定义Music类,里面有音乐名name,音乐时长times属性,并由播放play功能和返回本身属性信息的功能方法getinfo。

输出
音乐《想见你》正在播放中...时长为239秒
*/

public class Example {
    public static void main(String[] args) {
        Music music = new Music("想见你",239);
        music.Play();
        music.getinfo();
    }
}

class Music{
    String name;
    int times;

    public Music(String name,int times){
        this.name = name;
        this.times = times;
    }

    public void Play(){
        System.out.println("音乐《" + this.name + "》正在播放中...时长为" + this.times + "秒");
    }

    public String getinfo(){
        return "音乐" + name + "播放时间为" + times;
    }
}
/*
输出
i = 101
j = 100
101
101
*/

class Demo{
    int i = 100;
    public void m(){
        int j = i++;
        System.out.println("i = " + i);  //先赋值给了j再增加 101
        System.out.println("j = " + j);  //100
    }
}

public class Example {
    public static void main(String[] args) {
        Demo demo1 = new Demo();  //i = 100
        Demo demo2 = demo1;  //i = 100
        demo2.m();  //
        System.out.println(demo1.i);  //101
        System.out.println(demo2.i);  //101
    }
}
/*
创建一个Employee类
属性有 名字,性别,年龄,职位,薪水
提供三个构造方法 可以初始化
1.名字 性别 年龄 职位 薪水
2.名字 性别 年龄
3.职位 薪水
要求充分利用构造器
*/

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

    }
}

class Employee{
    String name;
    char gender;
    int age;
    String job;
    double sal;

    public Employee(String job,double sal){
        this.job = job;
        this.sal = sal;
    }

    public Employee(String name,char gender,int age){
        this.name = name;
        this.gender = gender;
        this.age = age;
    }

    public Employee(String name,char gender,int age,String job,double sal){
        this(name,gender,age);  //使用了前面的构造器 只能使用一个构造器 因为构造器的调用只能放在第一行
        this.job = job;
        this.sal = sal;
    }
}
/*
将对象作为参数传递给方法
1.定义一个Circle类,包含一个double型的radius属性代表圆的半径,findArea()方法返回圆的面积
2.定义一个类PassObject,在类中定义一个方法printAreas(),该方法的定义如下
public void printAreas (Circle c,int times)
3.在printAreas方法中打印输出1到times之间的每个整数半径值,以及对应的面积。
例如,times为5,则输出半径1,2,3,4,5,以及对应的圆的面积
4.在main方法中调用printAreas()方法,调用完毕后输出当前半径值。

输出:
radius	area
1.0		3.141592653589793
2.0		12.566370614359172
3.0		28.274333882308138
4.0		50.26548245743669
5.0		78.53981633974483
*/

public class Example {
    public static void main(String[] args) {
        Circle c = new Circle();
        PassObject passObject = new PassObject();
        passObject.printAreas(c,5);
    }
}

class Circle{
    double radius;
    public Circle(){
        //无参构造器
    }
    public Circle(double radius){
        this.radius = radius;
    }

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

    //添加一个方法setRadius
    public void setRadius(double radius){
        this.radius = radius;
    }
}

class PassObject{
    public void printAreas (Circle c,int times){
        System.out.println("radius\tarea");
        for (int i = 1;i <= times;i++){
            c.setRadius(i);  //调用public void setRadius(double radius)
            System.out.println((double)i + "\t\t" + c.findArea());  //传入public double findArea()
        }
    }
}

  1. 区分相同名字的类。
  2. 当类很多时,可以更好的管理类。
  3. 控制访问范围。 

实际上就是创建不同的文件夹来保存类文件。

需要使用到什么类就导入什么类

包的命名规则

只能包含数字、字母、下划线、小圆点。

不能以数字开头,不能是关键字或保留字。

命名规范

一般是小写字母+小圆点

例如:com.公司名.项目名.业务模块名 

包的使用细节

  1. 声明当前类所在的包,需要放在代码的最上面,一个类中最多只有一句package。
  2. import指令,位置放在package的下面,再类定义前面,可以有多句且没有顺序要求。

访问修饰符

  1. public,公开级别,对外公开
  2. protected,受保护级别,对子类和同一个包的类公开。
  3. 默认级别,没有修饰符号,向同一个包的类公开。
  4. private,私有级别,只有类本身可以访问,不对外公开。
    访问级别访问控制修饰符同类同包子类不同包
    公开public

    受保护

    protected
    默认没有修饰符
    私有private

访问修饰符的使用细节

  1. 修饰符可以用来修饰类中的属性,成员方法以及类。
  2. 只有默认的和public才能修饰类!并且遵循上述访问权限的特点。
  3. 成员方法的访问规则和属性一样。

按照如图下所示的结构进行创建 

com.Example.A代码

package com.Example;

public class A {

    //四个属性,分别使用不同的访问修饰符来修饰
    public int n1 = 100;
    protected int n2 = 200;
    int n3 = 300;
    private int n4 = 400;


    public void m1(){
        //该方法可以访问四个属性
        System.out.println("n1 = " + n1 + " n2 = " + n2 + " n3 = " + n3 + " n4 = " + n4);
    }
}

com.Example.B代码

package com.Example;

public class B {
    public void say(){
        A a = new A();  //同包,可以直接用
        //在同一个包下,可以访问public,protected和默认,不能访问private
        System.out.printf("n1 = " + a.n1 + " n2 = " + a.n2 + " n3 = " + a.n3);
    }
}

在输出语句时添加n4发现报错

说明private包只能在同类中访问。不同包和同包的不同类都不行。 

 com.Example.Test代码

package com.Example;

public class Test {
    public static void main(String[] args) {
        A a = new A();
        a.m1();
        B b = new B();
        b.say();
    }
}

com.xiaoyan.Test代码

package com.xiaoyan;

import com.Example.A;

public class Test {
    public static void main(String[] args) {
        A a = new A();
        //在不同包下,可以访问public修饰的属性或方法,但是不能访问protected,默认,private方法。
        System.out.println(a.n1);
    }
}

输出语句只能输出n1,因为n1是public,都可以访问,例如n2是protected,不同包就不能够访问,这里的Test是另外一个包中的代码块了。 

封装 

就是把抽象出的数据(属性)和对数据的操作(方法)封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作(方法),才能对数据进行操作。

封装的好处 

  1. 隐藏实现细节
  2. 可以对数据进行验证,保证安全合理 

封装步骤 

  1. 将属性进行私有化private(不能直接修改属性)。
  2. 提供一个公共的set方法,用于对属性判断并赋值。
  3. 提供一个公共的get方法,用于获取属性的值。

IDEA中,笔记本键入 Fn + Alt + Insert 可以开启封装的快捷键。

如果只是想要封装一个,点击即可,如果想要一次性封装多个,键盘按住Ctrl + 鼠标点击想要选择的几个属性。

然后点击OK即可。

为了之后的编写博客方便,简单的程序设计不再对文件结构进行详细解释,可以通过看程序第一行的package进行理解。 

package com.Example;

public class A {
    public static void main(String[] args) {
        Person person = new Person();
        person.setName("小焰");
        person.setAge(21);
        person.setSalary(30000);

        System.out.printf(person.info());
    }
}

class Person {
    public String name;  //名字公共
    private int age;  //年龄私有
    private double salary;  //工资私有

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        if (age >= 1 && age <= 120) {  //如果合理
            this.age = age;
        } else {
            System.out.println("年龄设置错误,需要在1-120区间内");
            this.age = 18;  //给一个默认年龄
        }
    }

    public double getSalary() {
        //可以在这里增加对当前对象的权限判断
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    //写一个方法,返回属性信息
    public String info() {
        return "姓名 = " + name + " 年龄 = " + age + " 薪水 = " + salary;
    }
}

封装与构造器

快捷键同上,笔记本是Fn + Alt + insert,不同的是选择的Constructor。

构建无参构造器

带属性的构造器,想要带几个参数,就选择对应的参数即可,跟上面的是一样的,选择之后点击OK即可构造。

  

package com.Example;

public class A {
    public static void main(String[] args) {
        Person person = new Person();
        person.setName("小焰");
        person.setAge(21);
        person.setSalary(30000);

        System.out.println(person.info());

        //如果我们自己使用构造器指定属性
        Person xiaoyan = new Person("xiaoyan",18,50000);
        System.out.println("=========================");
        System.out.println(xiaoyan.info());
    }
}

class Person {
    public String name;
    private int age;
    private double salary;

    public Person() {
    }

    public Person(String name, int age, double salary) {

//        this.name = name;
        this.age = age;
        this.salary = salary;

        //可以将set方法写在构造器中,这样仍然可以得到保证
        setName(name);
        setAge(age);
        setSalary(salary);
    }

    //写一个方法,返回属性信息
    public String info() {
        return "姓名 = " + name + " 年龄 = " + age + " 薪水 = " + salary;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }
}

例题

/*
创建程序,在其中定义两个类,Account和A类体会Java的封装性。
1.Account类要求具有属性:姓名(长度为2,3,4位),余额(必须 > 20),密码(必须是六位),
如果不满足,则给出提示信息,并给出默认值。
2.通过setXxx的方法给Account的属性赋值。
3.在A中测试


符合要求的输出:
姓名 = 小焰 余额 = 2000.0 密码 = 123456

不符合要求则输出:
姓名要求长度为2,3,4位,默认值为noname
余额必须 > 20,默认值为0
密码必须是六位,默认值为000000
姓名 = noname 余额 = 0.0 密码 = 000000
 */

package com.Example;

public class A {
    public static void main(String[] args) {
        Account account = new Account();
        account.setName("小焰");
        account.setBalance(2000);
        account.setPassword("123456");
        account.showInfo();
    }
}

class Account {
    //为了封装,将三个属性设置为私有
    private String name;
    private double balance;
    private String password;

    //提供无参构造器
    public Account() {
    }

    //提供有参构造器
    public Account(String name, double balance, String password) {
        this.name = name;
        this.balance = balance;
        this.password = password;
    }

    //显示账户信息
    public void showInfo(){
        System.out.println("姓名 = " + name + " 余额 = " + balance + " 密码 = " + password);
    }

    public String getName() {
        return name;
    }

    //姓名(长度为2,3,4位)
    public void setName(String name) {
        if (name.length() >= 2 && name.length() <= 4){
            this.name = name;
        } else {
            System.out.println("姓名要求长度为2,3,4位,默认值为noname");
            this.name = "noname";
        }
    }

    public double getBalance() {
        return balance;
    }

    //余额(必须 > 20)
    public void setBalance(double balance) {
        if (balance > 20) {
            this.balance = balance;
        } else {
            System.out.println("余额必须 > 20,默认值为0");
            this.balance = 0;
        }
    }

    public String getPassword() {
        return password;
    }

    //密码(必须是六位)
    public void setPassword(String password) {
        if (password.length() == 6) {
            this.password = password;
        } else {
            System.out.println("密码必须是六位,默认值为000000");
            this.password = "000000";
        }
    }
}

继承

继承可以解决代码复用,让我们的编程更加靠近人类思维,当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过extends来声明继承父类即可。

子类只用写自己所特有的方法就可以了。

子类也称xx的派生类。

子类可以继续被继承。

父类又叫超类,基类。

Example.java

/*
没有继承的时候输出:
小学生小焰正在考小学数学...
小学生姓名:小焰 年龄 = 8 成绩 = 98.0
大学生xiaoyan正在考小学数学...
大学生姓名:xiaoyan 年龄 = 18 成绩 = 80.0

冗余度太高,引入继承
 */

package com.Example;

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

        Pupil pupil = new Pupil();
        pupil.setName("小焰");
        pupil.setAge(8);
        pupil.test();;
        pupil.setScore(98);
        pupil.show();

        Graduate graduate = new Graduate();
        graduate.setName("xiaoyan");
        graduate.setAge(18);
        graduate.test();
        graduate.setScore(80);
        graduate.show();
    }
}

Graduate.java

package com.Example;

//大学生,模拟大学生考试情况
public class Graduate extends Pupil {
    private String name;
    private int age;
    private double score;

    public void test(){
        System.out.println("大学生" + name + "正在考小学数学...");
    }

    public void show(){
        System.out.println("大学生姓名:" + name + " 年龄 = " + age + " 成绩 = " + score);
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public void setName(String name) {
        this.name = name;
    }

    @Override
    public int getAge() {
        return age;
    }

    @Override
    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public double getScore() {
        return score;
    }

    @Override
    public void setScore(double score) {
        this.score = score;
    }
}

Pupil.java

package com.Example;

//小学生,模拟小学生考试情况
public class Pupil {
    private String name;
    private int age;
    private double score;

    public void test(){
        System.out.println("小学生" + name + "正在考小学数学...");
    }

    public void show(){
        System.out.println("小学生姓名:" + name + " 年龄 = " + age + " 成绩 = " + score);
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public double getScore() {
        return score;
    }

    public void setScore(double score) {
        this.score = score;
    }
}

引入继承

Test.java

package com.Example.improve;

public class Test {
    public static void main(String[] args) {
        Pupil pupil = new Pupil();
        pupil.setName("小焰");
        pupil.setAge(8);
        pupil.test();
        pupil.setScore(98);
        pupil.show();

        System.out.println("================");
        Graduate graduate = new Graduate();
        graduate.setName("xiaoyan");
        graduate.setAge(18);
        graduate.test();
        graduate.setScore(80);
        graduate.show();
    }
}

Student.java

package com.Example.improve;

//父类,是Pupil和Graduate的父类
public class Student {
    public String name;
    public int age;
    public double score;

    public void show(){
        System.out.println("小学生姓名:" + name + " 年龄 = " + age + " 成绩 = " + score);
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public double getScore() {
        return score;
    }

    public void setScore(double score) {
        this.score = score;
    }
}

Pupil.java

package com.Example.improve;

public class Pupil extends Student {
    public void test(){
        System.out.println("小学生" + name + "正在考小学数学...");
    }
}

Graduate.java

package com.Example.improve;

public class Graduate extends Student {
    public void test(){
        System.out.println("大学生" + name + "正在考小学数学...");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值