跳转至

Java面向对象与封装

约 1368 个字 479 行代码 1 张图片 预计阅读时间 11 分钟

封装

封装引入

面向对象三大特性:封装、继承和多态

在Java中,封装是将一类事物的属性和方法放到一个class类中,例如Person类

Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package com.epsda.java_class;

public class Person {
    // 属性
    String name;
    int age;
    Birthday birthday;

    // 方法
    public void print(){
        System.out.println("name = " + name + " " + "age = " + age + " " + "birthday: " + birthday.year + "/" + birthday.month + "/" + birthday.day);
    }
}

但是Person类中的成员此时可以被随意访问和修改,为了防止这种问题,从而出现了访问修饰符private

private修饰符

使用private修饰符修饰的成员无法在类外被访问

Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
package com.epsda.java_private;

public class Person {
    // 被priavte修饰后无法在类外访问
    private int age;
    private String name;

}

package com.epsda.java_private;

public class java_private {
    public static void main(String[] args) {
        Person person = new Person();
        // 被priavte修饰后无法在类外访问
        // person.age = 10;
        // person.name = "张三";

    }
}

但是封装之后需要对外提供使用的接口,在Java中称为getset方法

this关键字

当方法的局部变量和成员变量重名时,可以使用this关键字指代成员变量,防止出现重名时的访问均为局部变量

this关键字指代的是调用对象,其地址和对象地址相同

Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.epsda.java_private;

public class Person {
    // 展示this和对象地址
    public void showThis(){
        System.out.println(this);
    }
}

package com.epsda.java_private;

public class java_private {
    public static void main(String[] args) {
        Person person2 = new Person();
        System.out.println(person2);
        person2.showThis();
    }
}

输出结果
com.epsda.java_private.Person@154617c
com.epsda.java_private.Person@154617c

结合get/set方法,this关键字的使用

Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.epsda.java_private;

public class Person {
    // 被priavte修饰后无法在类外访问
    private int age;
    private String name;

    // 提供get和set方法
    public int getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }

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

构造函数

Java中创建对象时需要使用new,而new后面的实际上是在调用构造函数构造对象,如果类没有显式写构造函数,那么JVM会隐式生成一个无参数的构造函数,但是如果显式写了构造函数(包括无参数和有参数),那么编译器就不会再生成

Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
package com.epsda.java_private;

public class Person {
    // 被priavte修饰后无法在类外访问
    private int age;
    private String name;

    // 构造函数
    // 无参数构造
    public Person() {
    }

    // 有参数构造
    public Person(int age, String name) {
        this.age = age;
        this.name = name;
    }
}

当无参数构造函数和有参数构造函数共存时,此时出现方法重载现象,如果创建对象不传入参数,则调用无参数构造函数,否则调用有参数构造函数

Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.epsda.java_private;


/**
 * ClassName: java_private
 * Package: com.epsda.java_private
 * Description:
 *
 * @author 憨八嘎
 * @version v1.0
 */
public class java_private {
    public static void main(String[] args) {
        // 调用无参数构造函数
        Person person = new Person();
        // 通过get方法获取属性值
        System.out.println(person.getName() + " " + person.getAge());
        // 通过set方法设定属性值
        person.setAge(10);
        person.setName("李四");
        System.out.println(person.getName() + " " + person.getAge());

        // 调用有参数构造函数
        Person person1 = new Person(10, "张三");
        // 通过get函数获取属性值
        System.out.println(person1.getName() + " " + person1.getAge());
    }
}

输出结果:
null 0
李四 10
张三 10

JavaBean

标准JavaBean

JavaBean是Java语言编写类的一种标准规范。符合JavaBean 的类,要求:

  1. 类必须是具体的(非抽象 abstract)和公共的,public class 类名

  2. 并且具有无参数的构造方法,有参构造

  3. 成员变量私有化,并提供用来操作成员变量的setget 方法。

在Java项目中,一般有以下包及对应的功能

  1. com.epsda.controller -> 专门放和页面打交道的类(表现层)
  2. com.epsda.service -> 专门放业务处理的类 (业务层)
  3. com.epsda.dao -> 专门放和数据库打交道的类(持久层)
  4. com.epsda.pojo -> 专门放JavaBean类
  5. com.epsda.utils -> 专门放工具类

对于上方的Person类来说,即为一个标准的JavaBean结构

Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package com.epsda.java_private;

/**
 * ClassName: Person
 * Package: com.epsda.java_private
 * Description:
 *
 * @author 憨八嘎
 * @version v1.0
 */
public class Person {
    // 被priavte修饰后无法在类外访问
    private int age;
    private String name;

    // 构造函数
    // 无参数构造
    public Person() {
    }

    // 有参数构造
    public Person(int age, String name) {
        this.age = age;
        this.name = name;
    }

    // 提供get和set方法
    public int getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }

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

JavaBean中的成员与数据库的关系

  1. 类名 -> 表名
  2. 属性名 -> 列名
  3. 对象 -> 表中每一行数据
  4. 属性值 -> 表中单元格中的数据

对于Person类来说,Person表结构如下

name age
张三 10
李四 10

static关键字

static基本使用

当一个成员是多个对象共享时,可以使用static关键字对该成员进行修饰

static修饰的成员可以直接使用类名调用

定义一个Student类,教室学生共用,所以可以设置为static,其他为每一个对象特有的则可以不设置为static

Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package com.epsda.java_static;

/**
 * ClassName: Student
 * Package: com.epsda.java_static
 * Description:
 *
 * @author 憨八嘎
 * @version 1.0
 */
public class Student {
    // 学生特有属性
    private String name;
    private int age;
    // 学生共有属性
    private static int classroom;

    public Student() {
    }

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

    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 static int getClassroom() {
        return classroom;
    }

    public static void setClassroom(int classroom) {
        Student.classroom = classroom;
    }

    public void print() {
        System.out.println(name + " " + age + " " + classroom);
    }
}

测试类

Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.epsda.java_static;

/**
 * ClassName: java_static
 * Package: com.epsda.java_static
 * Description:
 *
 * @author 憨八嘎
 * @version 1.0
 */
public class java_static {
    public static void main(String[] args) {
        Student student1 = new Student("张三", 18);
        Student student2 = new Student("李四", 20);
        // 两个学生在一个班级时,直接用类名调用,而不是每一个单独设置
        Student.setClassroom(112);

        student1.print();
        student2.print();
    }
}

输出结果
张三 18 112
李四 20 112

static关键字访问特点

static关键字修饰的成员(成员变量和成员函数)与类同时加载到内存,所以会早于普通的成员变量,并且加载到内存堆区的静态域中,所以,在调用时需要注意下面的情况:

  1. 非静态成员可以访问静态成员

    Java
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    package com.epsda.java_static;
    
    /**
     * ClassName: java_static
     * Package: com.epsda.java_static
     * Description:
     *
     * @author 憨八嘎
     * @version 1.0
     */
    public class java_static {
        public static void main(String[] args) {
        }
    
        // 非静态成员
        public void func1() {
            // 非静态成员可以直接访问静态成员
            func2();
        }
    
        // 静态成员
        public static void func2(){
    
        }
    }
    
  2. 静态成员不可以访问非静态成员,除非创建对象,因为对象创建后,非静态成员也被创建了

    Java
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    package com.epsda.java_static;
    
    /**
     * ClassName: java_static
     * Package: com.epsda.java_static
     * Description:
     *
     * @author 憨八嘎
     * @version 1.0
     */
    public class java_static {
        public static void main(String[] args) {
            // func1(); 非静态成员不可以被静态成员直接调用
            java_static js = new java_static();
            js.func1();// 但是可以通过对象调用
        }
    
        // 非静态成员
        public void func1() {
    
        }
    }
    
  3. 非静态成员可以访问非静态成员,静态成员可以访问静态成员

Note

static成员在开发中一般可以指定一个工具类,工具类是指该类中是某一种对象的常见方法,该方法被修饰为static,便于使用类名直接调用。特殊地,工具类的构造方法全是private修饰

可变参数

在Java中,如果不确定函数参数的具体个数,可以使用可变参数进行代替,声明可变参数的方式如下

Java
1
数据类型...变量名

例如,求出n个整数相加之和

Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.epsda.multiple_variables;

/**
 * ClassName: Multi_variables
 * Package: com.epsda.multiple_variables
 * Description:
 *
 * @author 憨八嘎
 * @version 1.0
 */
public class Multi_variables {
    public static void main(String[] args) {
        int ans1 = sum(1,2,3);
        int ans2 = sum(1,2,3,4);
        int ans3 = sum(1,2,3,4,5);
        System.out.println("ans1 = " + ans1);
        System.out.println("ans2 = " + ans2);
        System.out.println("ans3 = " + ans3);
    }

    public static int sum(int...arr) {
        int ans = 0;
        for (int i = 0; i < arr.length; i++) {
            ans += arr[i];
        }
        return ans;
    }
}

输出结果
ans1 = 6
ans2 = 10
ans3 = 15

在Java中,可变参数实际上是一个数组类型,所以可以使用数组的方式进行遍历

一个形参位置只能有一个可变参数,并且如果有其他参数时,可变参数必须放在最后一个参数的位置

Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.epsda.multiple_variables;

/**
 * ClassName: Multi_variables
 * Package: com.epsda.multiple_variables
 * Description:
 *
 * @author 憨八嘎
 * @version 1.0
 */
public class Multi_variables {
    public static void main(String[] args) {
        // 第一个实参给函数的一个形参,剩下的全给可变参数
        String ans = concat("-", "字符串1", "字符串2");
        System.out.println("ans = " + ans);
    }

    // 多个参数,可变参数放在最后
    public static String concat(String regex, String... arr) {
        String ans = "";
        for (int i = 0; i < arr.length; i++) {
            if (i == arr.length - 1) {
                ans += arr[i];
            } else {
                ans += arr[i] + regex;
            }
        }

        return ans;
    }
}

输出结果
ans = 字符串1-字符串2

对象数组与传值/址调用

对象数组

所谓对象数组,即数组中的元素是对象

Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.epsda.array;

/**
 * ClassName: object_array
 * Package: com.epsda.array
 * Description:
 *
 * @author 憨八嘎
 * @version 1.0
 */
public class object_array {
    public static void main(String[] args) {
        // 创建对象数组
        Person arr[] = new Person[3];
        // 创建对象并存入数组中
        for (int i = 0; i < arr.length; i++) {
            // 数组中的每一个元素都是Person的匿名对象
            arr[i] = new Person(18,"姓名");
            System.out.println(arr[i].getName()+" "+arr[i].getAge());
        }
    }
}

传值调用与传址调用

在Java中,基本数据类型实参传递给方法的形参时只是传值调用,但是对于引用类型来说,传递给方法形参的实参都是地址,所以是传址调用

Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package com.epsda.call;

/**
 * ClassName: Call
 * Package: com.epsda.call
 * Description:
 *
 * @author 憨八嘎
 * @version 1.0
 */
public class Call {
    public static void main(String[] args) {
        // 基本数据类型
        int a = 10;
        int b = 20;
        sum(a, b);
        System.out.println("a = " + a);// 方法内修改不影响main函数中的a和b,传值调用
        System.out.println("b = " + b);
        System.out.println("---------------------------");
        // 引用数据类型
        int arr[] = {2, 3, 4, 5};
        change(arr);// 方法内的修改影响main函数中的引用类型
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
    }

    // 传值调用
    public static void sum(int a, int b) {
        a = 20;
        b = 40;
        System.out.println("a = " + a);
        System.out.println("b = " + b);
    }

    // 传址调用
    public static void change(int[] arr) {
        for (int i = 0; i < arr.length; i++) {
            arr[i] = i;
            System.out.print(arr[i] + " ");
        }
        System.out.println();
    }
}

输出结果
a = 20
b = 40
a = 10
b = 20
---------------------------
0 1 2 3
0 1 2 3 

Note

需要注意的是String类型引用,在Java中,字符串是不可改变的量,所以如果在方法内尝试对实参的String类型值进行改变并不会影响实参。每一个字符串都有对应的地址,当指向字符串的实参传入方法中,在方法内改变该实参对应的形参内容只是改变形参指向的地址值,并不会改变实参指向的地址值,本质这里还是传值调用,例如:

Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.epsda.call;

/**
 * ClassName: Call
 * Package: com.epsda.call
 * Description:
 *
 * @author 憨八嘎
 * @version 1.0
 */
public class Call {
    public static void main(String[] args) {
        String arr1 = "字符串";
        change(arr1);// 方法内的修改不影响main函数arr1
        System.out.print(arr1);
    }


    public static void change(String arr1) {
        arr1 = "修改字符串";
        System.out.print(arr1);
        System.out.println();
    }
}

输出结果
修改字符串
字符串

命令行参数

Java中的main方法有一个形参String[] args,当需要临时测试方法时,可以通过这个形参传递值

Java
1
2
3
4
5
6
7
public class CommandParam{
    public static void main(String[] args){
        for(int i = 0; i < args.length; i++){
            System.out.println("第" + (i+1) + "个参数的值是:" + args[i]);
        }
    }
}

在命令行中输入命令:

Java
1
javac CommandParam.java
Java
1
java CommandParam 字符串1 字符串2

此时即可输出下面的内容:

image-20240702212128506