自学Java之第六章:面向对象编程(基础)

目录

1.面向对象编程概述(了解)

1.1 程序设计的思路

1.2 由实际问题考虑如何设计程序

2. Java语言的基本元素:类和对象

2.1 类和对象概述

2.2 类的成员概述

2.3 面向对象完成功能的三步骤(重要)

2.4 匿名对象 (anonymous object)

3. 对象的内存解析

3.1 JVM内存结构划分

4. 类的成员之一:成员变量(field)

4.1 如何声明成员变量

4.2 成员变量 vs 局部变量

1、变量的分类:成员变量与局部变量

2、成员变量 与 局部变量 的对比

3、对象属性的默认初始化赋值

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

5.1 方法(method、函数)的理解

5.2 如何声明方法

5.3 如何调用实例方法

5.4 使用的注意点 

5.5 关键字return的使用

5.6 方法调用内存分析

6. 对象数组

7.方法的重载

7.1 概念及特点

7.2 可变个数的形参

7.3 方法的参数传递机制

7.4 递归(recursion)方法

8. 关键字:package、import

8.1 package(包)

8.2 import(导入)

9. 面向对象特征一:封装性(encapsulation)

9.1 为什么需要封装?

9.2 何为封装性?

9.3 Java如何实现数据封装

9.4 封装性的体现

10. 类的成员之三:构造器(Constructor)

10.1 构造器的作用

10.2 构造器的语法格式

11. 阶段性知识补充

11.1 类中属性赋值过程

11.2 JavaBean

11.3 UML类图


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

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

1.面向对象编程概述(了解)

1.1 程序设计的思路

        面向对象,是软件开发中的一类编程风格、开发范式。除了面向对象,还有面向过程指令式编程函数式编程。在所有的编程范式中,我们接触最多的还是面向过程和面向对象两种。

        早期先有面向过程思想,随着软件规模的扩大,问题复杂性的提高,面向过程的弊端越来越明显,出现了面向对象思想并成为目前主流的方式。

1. 面向过程的程序设计思想(Process-Oriented Programming),简称POP

        关注的焦点是过程:过程就是操作数据的步骤。如果某个过程的实现代码重复出现,那么就可以把这个过程抽取为一个函数。这样就可以大大简化冗余代码,便于维护。

2. 面向对象的程序设计思想( Object Oriented Programming),简称OOP

        关注的焦点是:在计算机程序设计过程中,参照现实中事物,将事物的属性特征、行为特征抽象出来,用类来表示。

1.2 由实际问题考虑如何设计程序

思考1:如何开车?

面向过程思想思考问题时,我们首先思考“怎么按步骤实现?”并将步骤对应成方法,一步一步,最终完成。 这个适合简单任务,不需要过多协作的情况。针对如何开车,可以列出步骤:

 面向过程适合简单、不需要协作的事务,重点关注如何执行。

思考2:如何造车?

造车太复杂,需要很多协作才能完成。此时我们思考的是“车怎么设计?”,而不是“怎么按特定步骤造车的问题”。这就是思维方式的转变,前者就是面向对象思想。所以,面向对象(Oriented-Object)思想更契合人的思维模式。

用面向对象思想思考“如何设计车”:

 自然地,我们就会从“车由什么组成”开始思考。发现,车由如下结构组成:

我们找轮胎厂完成制造轮胎的步骤,发动机厂完成制造发动机的步骤,...;这样,大家可以同时进行车的制造,最终进行组装,大大提高了效率。但是,具体到轮胎厂的一个流水线操作,仍然是有步骤的,还是离不开面向过程思维!

因此,面向对象可以帮助我们从宏观上把握、从整体上分析整个系统。 但是,具体到实现部分的微观操作(就是一个个方法),仍然需要面向过程的思路去处理。注意:我们千万不要把面向过程和面向对象对立起来。他们是相辅相成的。面向对象离不开面向过程!

2. Java语言的基本元素:类和对象

2.1 类和对象概述

1、什么是类

        类:具有相同特征的事物的抽象描述,是抽象的、概念上的定义。

2、什么是对象

        对象:实际存在的该类事物的每个个体,是具体的,因而也称为实例(instance)

2.2 类的成员概述

        面向对象程序设计的重点是类的设计

类的设计,其实就是类的成员的设计

类,是一组相关属性行为的集合,这也是类最基本的两个成员。

属性:该类事物的状态信息。对应类中的成员变量

  • 成员变量 <=> 属性 <=> Field

行为:该类事物要做什么操作,或者基于事物的状态能做什么。对应类中的成员方法

  • (成员)方法 <=> 函数 <=> Method

2.3 面向对象完成功能的三步骤(重要)

步骤1:类的定义

类的定义使用关键字:class。格式如下:

[修饰符] class 类名{
属性声明;
    方法声明;
}

public class Person{
    //声明属性age
    int age ;	                   
    
    //声明方法showAge()
    public void eat() {        
	    System.out.println("人吃饭");
    }
}

 步骤2:对象的创建

创建对象,使用关键字:new

创建对象语法: 

//方式1:给创建的对象命名
//把创建的对象用一个引用数据类型的变量保存起来,这样就可以反复使用这个对象了
类名 对象名 = new 类名();
//方式2:
new 类名()//也称为匿名对象

public static void main(String[] args){
		//创建Person类的对象
		Person per = new Person();
		//创建Dog类的对象
		Dog dog = new Dog();
	}

步骤3:对象调用属性或方法 

对象是类的一个实例,必然具备该类事物的属性和行为(即方法)。

使用"对象名.属性" 或 "对象名.方法"的方式访问对象成员(包括属性和方法)

//声明Animal类
public class Animal { //动物类
    public int legs;

    public void eat() {
        System.out.println("Eating.");
    }

    public void move() {
        System.out.println("Move.");
    }
}
//声明测试类
public class AnimalTest {
    public static void main(String args[]) {
        //创建对象
        Animal xb = new Animal();
        xb.legs = 4;//访问属性
        System.out.println(xb.legs);
        xb.eat();//访问方法
        xb.move();//访问方法
    }
}

2.4 匿名对象 (anonymous object)

我们也可以不定义对象的句柄,而直接调用这个对象的方法。这样的对象叫做匿名对象。

  • 如:new Person().shout();

使用情况

  • 如果一个对象只需要进行一次方法调用,那么就可以使用匿名对象。
  • 我们经常将匿名对象作为实参传递给一个方法调用。

3. 对象的内存解析

3.1 JVM内存结构划分

HotSpot Java虚拟机的架构图如下。其中我们主要关心的是运行时数据区部分(Runtime Data Area)。

其中:

堆(Heap):此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。这一点在Java虚拟机规范中的描述是:所有的对象实例以及数组都要在堆上分配。

栈(Stack):是指虚拟机栈。虚拟机栈用于存储局部变量等。局部变量表存放了编译期可知长度的各种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference类型,它不等同于对象本身,是对象在堆内存的首地址)。 方法执行完,自动释放。

方法区(Method Area):用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

4. 类的成员之一:成员变量(field)

4.1 如何声明成员变量

语法格式:

[修饰符1] class 类名{
    [修饰符2] 数据类型 成员变量名 [= 初始化值]; 
}

说明:

  • 位置要求:必须在类中,方法外
    • 修饰符2(暂不考虑)
      • 常用的权限修饰符有:private、缺省、protected、public
      • 其他修饰符:static、final
    • 数据类型
      • 任何基本数据类型(如int、Boolean) 或 任何引用数据类型。
    • 成员变量名
      • 属于标识符,符合命名规则和规范即可。
    • 初始化值
      • 根据情况,可以显式赋值;也可以不赋值,使用默认值
public class Person{
	private int age;             //声明private变量 age
	public String name = “Lila”;    //声明public变量 name
}

4.2 成员变量 vs 局部变量

1、变量的分类:成员变量与局部变量

在方法体外,类体内声明的变量称为成员变量。

在方法体内部等位置声明的变量称为局部变量。

 其中,static可以将成员变量分为两大类,静态变量和非静态变量。其中静态变量又称为类变量,非静态变量又称为实例变量或者属性。

2、成员变量 与 局部变量 的对比

相同点:

  • 变量声明的格式相同: 数据类型 变量名 = 初始化值
  • 变量必须先声明、后初始化、再使用。
  • 变量都有其对应的作用域。只在其作用域内是有效的

不同点:

1、声明位置和方式 (1)实例变量:在类中方法外 (2)局部变量:在方法体{}中或方法的形参列表、代码块中

2、在内存中存储的位置不同 (1)实例变量:堆 (2)局部变量:栈

3、生命周期 (1)实例变量:和对象的生命周期一样,随着对象的创建而存在,随着对象被GC回收而消亡,  而且每一个对象的实例变量是独立的。 (2)局部变量:和方法调用的生命周期一样,每一次方法被调用而在存在,随着方法执行的结束而消亡,  而且每一次方法调用都是独立。

4、作用域 (1)实例变量:通过对象就可以使用,本类中直接调用,其他类中“对象.实例变量” (2)局部变量:出了作用域就不能使用

5、修饰符(后面来讲) (1)实例变量:public,protected,private,final,volatile,transient等 (2)局部变量:final

6、默认值 (1)实例变量:有默认值 (2)局部变量:没有,必须手动初始化。其中的形参比较特殊,靠实参给它初始化。

 3、对象属性的默认初始化赋值

当一个对象被创建时,会对其中各种类型的成员变量自动进行初始化赋值。

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

5.1 方法(method、函数)的理解

方法是类或对象行为特征的抽象,用来完成某个功能操作。在某些语言中也称为函数过程

将功能封装为方法的目的是,可以实现代码重用,减少冗余,简化代码

Java里的方法不能独立存在,所有的方法必须定义在类里。

举例1:

  • Math.random()的random()方法
  • Math.sqrt(x)的sqrt(x)方法
  • System.out.println(x)的println(x)方法
  • new Scanner(System.in).nextInt()的nextInt()方法
  • Arrays类中的binarySearch()方法、sort()方法、equals()方法
 public class Person{
    private int age;
    public int getAge()  {  //声明方法getAge()
		return age; 
    }
    public void setAge(int i) {  //声明方法setAge
		age = i;        //将参数i的值赋给类的成员变量age
    }
}

5.2 如何声明方法

1、声明方法的语法格式

[修饰符] 返回值类型 方法名([形参列表])[throws 异常列表]{
        方法体的功能代码
}

(1)一个完整的方法 = 方法头 + 方法体。

方法头就是[修饰符] 返回值类型 方法名([形参列表])[throws 异常列表],也称为方法签名。通常调用方法时只需要关注方法头就可以,从方法头可以看出这个方法的功能和调用格式。

方法体就是方法被调用后要执行的代码。对于调用者来说,不了解方法体如何实现的,并不影响方法的使用。

(2)方法头可能包含5个部分

修饰符可选的。方法的修饰符也有很多,例如:public、protected、private、static、abstract、native、final、synchronized等,后面会一一学习。

  • 其中,权限修饰符有public、protected、private。在讲封装性之前,我们先默认使用pulbic修饰方法。
  • 其中,根据是否有static,可以将方法分为静态方法和非静态方法。其中静态方法又称为类方法,非静态方法又称为实例方法。咱们在讲static前先学习实例方法。

返回值类型 表示方法运行的结果的数据类型,方法执行后将结果返回到调用者。

  • 无返回值,则声明:void
  • 有返回值,则声明出返回值类型(可以是任意类型)。与方法体中“return 返回值”搭配使用

方法名属于标识符,命名时遵循标识符命名规则和规范,“见名知意”

形参列表表示完成方法体功能时需要外部提供的数据列表。可以包含零个,一个或多个参数。

  • 无论是否有参数,()不能省略
  • 如果有参数,每一个参数都要指定数据类型和参数名,多个参数之间使用逗号分隔,例如:
    • 二个参数: (数据类型1 参数1, 数据类型2 参数2)
    • 一个参数: (数据类型 参数名)
  • 参数的类型可以是基本数据类型、引用数据类型

(3)方法体:方法体必须有{}括起来,在{}中编写完成方法功能的代码

  (4)关于方法体中return语句的说明:

return语句的作用是结束方法的执行,并将方法的结果返回去

如果返回值类型不是void,方法体中必须保证一定有 return 返回值; 语句,并且要求该返回值结果的类型与声明的返回值类型一致或兼容。

如果返回值类型为void时,方法体中可以没有return语句,如果要用return语句提前结束方法的执行,那么return后面不能跟返回值,直接写return ; 就可以。

return语句后面就不能再写其他代码了,否则会报错:Unreachable code

public class MethodDefineDemo {
    /**
     * 无参无返回值方法的演示
     */
    public void sayHello(){
        System.out.println("hello");
    }

    /**
     * 有参无返回值方法的演示
     * @param length int 第一个参数,表示矩形的长
     * @param width int 第二个参数,表示矩形的宽
     * @param sign char 第三个参数,表示填充矩形图形的符号
     */
    public void printRectangle(int length, int width, char sign){
        for (int i = 1; i <= length ; i++) {
            for(int j=1; j <= width; j++){
                System.out.print(sign);
            }
            System.out.println();
        }
    }

    /**
     * 无参有返回值方法的演示
     * @return
     */
    public int getIntBetweenOneToHundred(){
        return (int)(Math.random()*100+1);
    }
    
    /**
     * 有参有返回值方法的演示
     * @param a int 第一个参数,要比较大小的整数之一
     * @param b int 第二个参数,要比较大小的整数之二
     * @return int 比较大小的两个整数中较大者的值
     */
    public int max(int a, int b){
        return a > b ? a : b;
    }
}

5.3 如何调用实例方法

方法通过方法名被调用,且只有被调用才会执行。

1、方法调用语法格式

对象.方法名([实参列表])

//1、创建Scanner的对象
Scanner input = new Scanner(System.in);//System.in默认代表键盘输入

//2、提示输入xx
System.out.print("请输入一个整数:"); //对象.非静态方法(实参列表)

//3、接收输入内容
int num = input.nextInt();  //对象.非静态方法()

5.4 使用的注意点 

  (1)必须先声明后使用,且方法必须定义在类的内部

(2)调用一次就执行一次,不调用不执行。

(3)方法中可以调用类中的方法或属性,不可以在方法内部定义方法。

5.5 关键字return的使用

return在方法中的作用:

  • 作用1:结束一个方法
  • 作用2:结束一个方法的同时,可以返回数据给方法的调用者

注意点:在return关键字的直接后面不能声明执行语句

5.6 方法调用内存分析

方法没有被调用的时候,都在方法区中的字节码文件(.class)中存储。

方法被调用的时候,需要进入到栈内存中运行。方法每调用一次就会在栈中有一个入栈动作,即给当前方法开辟一块独立的内存区域,用于存储当前方法的局部变量的值。

当方法执行结束后,会释放该内存,称为出栈,如果方法有返回值,就会把结果返回调用处,如果没有返回值,就直接结束,回到调用处继续执行下一条指令。

栈结构:先进后出,后进先出。

6. 对象数组

        数组的元素可以是基本数据类型,也可以是引用数据类型。当元素是引用类型中的类时,我们称为对象数组。

1.注意点

        对象数组,首先要创建数组对象本身,即确定数组的长度,然后再创建每一个元素对象,如果不创建,数组的元素的默认值就是null,所以很容易出现空指针异常NullPointerException

2.练习

(1)定义矩形类Rectangle,包含长、宽属性,area()返回矩形面积的方法,perimeter()返回矩形周长的方法,String getInfo()返回圆对象的详细信息(如:长、宽、面积、周长等数据)的方法

(2)在测试类中创建长度为3的Rectangle[]数组,用来装3个矩形对象,并给3个矩形对象的长分别赋值为10,20,30,宽分别赋值为5,15,25,遍历输出

public class Rectangle {
    double length;
    double width;

    public double area(){//面积
        return length * width;
    }

    public double perimeter(){//周长
        return 2 * (length + width);
    }

    public String getInfo(){
        return "长:" + length +
                ",宽:" + width +
                ",面积:" + area() +
                ",周长:" + perimeter();
    }
}


public class ObjectArrayTest {
    public static void main(String[] args) {
        //声明并创建一个长度为3的矩形对象数组
        Rectangle[] array = new Rectangle[3];

        //创建3个矩形对象,并为对象的实例变量赋值,
        //3个矩形对象的长分别是10,20,30
        //3个矩形对象的宽分别是5,15,25
        //调用矩形对象的getInfo()返回对象信息后输出
        for (int i = 0; i < array.length; i++) {
            //创建矩形对象
            array[i] = new Rectangle();

            //为矩形对象的成员变量赋值
            array[i].length = (i+1) * 10;
            array[i].width = (2*i+1) * 5;

            //获取并输出对象对象的信息
            System.out.println(array[i].getInfo());
        }
    }
}

7.方法的重载

7.1 概念及特点

方法重载在同一个类中,允许存在一个以上的同名方法,只要它们的参数列表不同即可。

  • 参数列表不同,意味着参数个数或参数类型的不同

重载的特点:与修饰符、返回值类型无关,只看参数列表,且参数列表必须不同。(参数个数或参数类型)。调用时,根据方法参数列表的不同来区别。

重载方法调用:JVM通过方法的参数列表,调用匹配的方法。

  • 先找个数、类型最匹配的
  • 再找个数和类型可以兼容的,如果同时多个方法可以兼容将会报错

7.2 可变个数的形参

JDK 5.0 中提供了Varargs(variable number of arguments)机制。即当定义一个方法时,形参的类型可以确定,但是形参的个数不确定,那么可以考虑使用可变个数的形参。

格式:

方法名(参数的类型名 ...参数名)

//JDK 5.0以前:采用数组形参来定义方法,传入多个同一类型变量
public static void test(int a ,String[] books);

//JDK5.0:采用可变个数形参来定义方法,传入多个同一类型变量
public static void test(int a ,String...books);

特点:

        可变参数:方法参数部分指定类型的参数个数是可变多个:0个,1个或多个

        可变个数形参的方法与同名的方法之间,彼此构成重载

        可变参数方法的使用与方法参数部分使用数组是一致的,二者不能同时声明,否则报错。

        方法的参数部分有可变形参,需要放在形参声明的最后

        在一个方法的形参中,最多只能声明一个可变个数的形参

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

public class StringTools {
    String concat(char seperator, String... args){
        String str = "";
        for (int i = 0; i < args.length; i++) {
            if(i==0){
                str += args[i];
            }else{
                str += seperator + args[i];
            }
        }
        return str;
    }
}


public static void main(String[] args) {
        StringTools tools = new StringTools();

        System.out.println(tools.concat('-'));
        System.out.println(tools.concat('-',"hello"));
        System.out.println(tools.concat('-',"hello","world"));
        System.out.println(tools.concat('-',"hello","world","java"));
    }

7.3 方法的参数传递机制

1.形参和实参

形参(formal parameter):在定义方法时,方法名后面括号()中声明的变量称为形式参数,简称形参。

实参(actual parameter):在调用方法时,方法名后面括号()中的使用的值/变量/表达式称为实际参数,简称实参。

2.参数传递机制:值传递

Java里方法的参数传递方式只有一种:值传递。 即将实际参数值的副本(复制品)传入方法内,而参数本身不受影响。

形参是基本数据类型:将实参基本数据类型变量的“数据值”传递给形参

形参是引用数据类型:将实参引用数据类型变量的“地址值”传递给形参

例子:

1、形参是基本数据类型:编写方法,交换两个整型变量的值

public static void main(String[] args) {
		int m = 10;
		int n = 20;
		
		System.out.println("m = " + m + ", n = " + n);
		//交换m和n的值
//		int temp = m;
//		m = n;
//		n = temp;
		
		ValueTransferTest1 test = new ValueTransferTest1();
		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;
	}

2.形参是引用数据类型

public static void main(String[] args) {
		
		Data d1 = new Data();
		d1.m = 10;
		d1.n = 20;
		
		System.out.println("m = " + d1.m + ", n = " + d1.n);
		
		//实现 换序
		ValueTransferTest2 test = new ValueTransferTest2();
		test.swap(d1);	
		System.out.println("m = " + d1.m + ", n = " + d1.n);
		
	}
	public void swap(Data data){
		int temp = data.m;
		data.m = data.n;
		data.n = temp;
	}

class Data{
	int m;
	int n;
}

7.4 递归(recursion)方法

递归方法调用:方法自己调用自己的现象就称为递归。

递归的分类: 直接递归、间接递归。

直接递归:方法自身调用自己。

 public void methodA(){
	methodA();
}

间接递归:可以理解为A()方法调用B()方法,B()方法调用C()方法,C()方法调用A()方法。

 public static void A(){
	B();
}

public static void B(){
	C();
}

public static void C(){
	A();
}

说明

递归方法包含了一种隐式的循环

递归方法会重复执行某段代码,但这种重复执行无须循环控制。

递归一定要向已知方向递归,否则这种递归就变成了无穷递归,停不下来,类似于死循环。最终发生栈内存溢出

例子:计算1 ~ n的和

public static void main(String[] args) {
        RecursionDemo demo = new RecursionDemo();
		//计算1~num的和,使用递归完成
		int num = 5;
      	// 调用求和的方法
		int sum = demo.getSum(num);
      	// 输出结果
		System.out.println(sum);
		
	}
  	/*
  	  通过递归算法实现.
  	  参数列表:int 
  	  返回值类型: int 
  	*/
	public int getSum(int num) {
      	/* 
      	   num为1时,方法返回1,
      	   相当于是方法的出口,num总有是1的情况
      	*/
		if(num == 1){
			return 1;
		}
      	/*
          num不为1时,方法返回 num +(num-1)的累和
          递归调用getSum方法
        */
		return num + getSum(num-1);
	}

8. 关键字:package、import

8.1 package(包)

package,称为包,用于指明该文件中定义的类、接口等结构所在的包。

1.语法格式

package 顶层包名.子包名 ;

说明:

  • 一个源文件只能有一个声明包的package语句
  • package语句作为Java源文件的第一条语句出现。若缺省该语句,则指定为无名包。
  • 包名,属于标识符,满足标识符命名的规则和规范(全部小写)、见名知意
  • 包通常使用所在公司域名的倒置:com.atguigu.xxx。
  • 大家取包名时不要使用"java.xx"包
  • 包对应于文件系统的目录,package语句中用 “.” 来指明包(目录)的层次,每.一次就表示一层文件目录。
  • 同一个包下可以声明多个结构(类、接口),但是不能定义同名的结构(类、接口)。不同的包下可以定义同名的结构(类、接口)

2.包的作用

  • 包可以包含类和子包,划分项目层次,便于管理
  • 帮助管理大型软件系统:将功能相近的类划分到同一个包中。比如:MVC的设计模式
  • 解决类命名冲突的问题
  • 控制访问权限

8.2 import(导入)

为了使用定义在其它包中的Java类,需用import语句来显式引入指定包下所需要的类。相当于import语句告诉编译器到哪里去寻找这个类

1.语法格式

import 包名.类名 ;

注意事项

  • import语句,声明在包的声明和类的声明之间。
  • 如果需要导入多个类或接口,那么就并列显式多个import语句即可
  • 如果使用a.*导入结构,表示可以导入a包下的所有的结构。举例:可以使用java.util.*的方式,一次性导入util包下所有的类或接口。
  • 如果导入的类或接口是java.lang包下的,或者是当前包下的,则可以省略此import语句。
  • 如果已经导入java.a包下的类,那么如果需要使用a包的子包下的类的话,仍然需要导入。
  • 如果在代码中使用不同包下的同名的类,那么就需要使用类的全类名的方式指明调用的是哪个类。
  • (了解)import static组合的使用:调用指定类或接口下的静态的属性或方法

9. 面向对象特征一:封装性(encapsulation)

9.1 为什么需要封装?

        随着我们系统越来越复杂,类会越来越多,那么类之间的访问边界必须把握好,面向对象的开发原则要遵循“高内聚、低耦合”。

        高内聚、低耦合是软件工程中的概念,也是UNIX 操作系统设计的经典原则。

        内聚,指一个模块内各个元素彼此结合的紧密程度;耦合指一个软件结构内不同模块之间互连程度的度量。内聚意味着重用和独立,耦合意味着多米诺效应牵一发动全身。

而“高内聚,低耦合”的体现之一:

  • 高内聚:类的内部数据操作细节自己完成,不允许外部干涉;
  • 低耦合:仅暴露少量的方法给外部使用,尽量方便外部调用。

9.2 何为封装性?

        所谓封装,就是把客观事物封装成抽象概念的类,并且类可以把自己的数据和方法只向可信的类或者对象开放,向没必要开放的类或者对象隐藏信息。

通俗的讲,把该隐藏的隐藏起来,该暴露的暴露出来。这就是封装性的设计思想。

9.3 Java如何实现数据封装

  • 实现封装就是控制类或成员的可见性范围。这就需要依赖访问控制修饰符,也称为权限修饰符来控制。
  • 权限修饰符:publicprotected缺省private。具体访问范围如下:

    修饰符

    本类内部

    本包内

    其他包的子类

    其他包非子类

    private

    ×

    ×

    ×

    缺省

    ×

    ×

    protected

    ×

    public

  • 具体修饰的结构:
    • 外部类:public、缺省
    • 成员变量、成员方法、构造器、成员内部类:public、protected、缺省、private

9.4 封装性的体现

1.成员变量/属性私有化

概述:私有化类的成员变量,提供公共的get和set方法,对外暴露获取和修改属性的功能。

实现步骤:

 使用 private 修饰成员变量

private 数据类型 变量名 ;

public class Person {
    private String name;
  	private int age;
    private boolean marry;
}

 提供 getXxx方法 / setXxx 方法,可以访问成员变量

public class Person {
    private String name;
  	private int age;
    private boolean marry;

	public void setName(String n) {
		name = n;
    }

    public String getName() {
        return name;
	}

    public void setAge(int a) {
        age = a;
    }

    public int getAge() {
        return age;
    }
    
    public void setMarry(boolean m){
        marry = m;
    }
    
    public boolean isMarry(){
        return marry;
    }
}

成员变量封装的好处:

  • 让使用者只能通过事先预定的方法来访问数据,从而可以在该方法里面加入控制逻辑,限制对成员变量的不合理访问。还可以进行数据检查,从而有利于保证对象信息的完整性。
  • 便于修改,提高代码的可维护性。主要说的是隐藏的部分,在内部修改了,如果其对外可以的访问方式不变的话,外部根本感觉不到它的修改。例如:Java8->Java9,String从char[]转为byte[]内部实现,而对外的方法不变,我们使用者根本感觉不到它内部的修改。

2.私有化方法

注意:

开发中,一般成员实例变量都习惯使用private修饰,再提供相应的public权限的get/set方法访问。

对于final的实例变量,不提供set()方法。(后面final关键字的时候讲)

        对于static final的成员变量,习惯上使用public修饰。

10. 类的成员之三:构造器(Constructor)

我们new完对象时,所有成员变量都是默认值,如果我们需要赋别的值,需要挨个为它们再赋值,太麻烦了。我们能不能在new对象时,直接为当前对象的某个或所有成员变量直接赋值呢?

可以,Java给我们提供了构造器(Constructor),也称为构造方法

10.1 构造器的作用

new对象,并在new对象的时候为实例变量赋值。

举例:Person p = new Person(“Peter”,15);

10.2 构造器的语法格式

[修饰符] class 类名{
    [修饰符] 构造器名(){
     // 实例初始化代码
    }
    [修饰符] 构造器名(参数列表){
        // 实例初始化代码
    }
}

说明:

        构造器名必须与它所在的类名必须相同。

        它没有返回值,所以不需要返回值类型,也不需要void。

        构造器的修饰符只能是权限修饰符,不能被其他任何修饰。比如,不能被static、final、synchronized、abstract、native修饰,不能有return语句返回值。

public class Student {
    private String name;
    private int age;

    // 无参构造
    public Student() {}

    // 有参构造
    public Student(String n,int a) {
        name = n;
        age = a;
    }

    public String getName() {
        return name;
    }
    public void setName(String n) {
        name = n;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int a) {
        age = a;
    }

    public String getInfo(){
        return "姓名:" + name +",年龄:" + age;
    }
}
public class TestStudent {
    public static void main(String[] args) {
        //调用无参构造创建学生对象
        Student s1 = new Student();

        //调用有参构造创建学生对象
        Student s2 = new Student("张三",23);

        System.out.println(s1.getInfo());
        System.out.println(s2.getInfo());
    }
}

11. 阶段性知识补充

11.1 类中属性赋值过程

1、在类的属性中,可以有哪些位置给属性赋值?

① 默认初始化

② 显式初始化

③ 构造器中初始化

④ 通过"对象.属性"或"对象.方法"的方式,给属性赋值

2、这些位置执行的先后顺序是怎样?

顺序:① - ② - ③ - ④

3、说明:

上述中的①、②、③在对象创建过程中,只执行一次。

④ 是在对象创建后执行的,可以根据需求多次执行。

11.2 JavaBean

JavaBean是一种Java语言写成的可重用组件。

  • 好比你做了一个扳手,这个扳手会在很多地方被拿去用。这个扳手也提供多种功能(你可以拿这个扳手扳、锤、撬等等),而这个扳手就是一个组件。

所谓JavaBean,是指符合如下标准的Java类:

  • 类是公共的
  • 有一个无参的公共的构造器
  • 有属性,且有对应的get、set方法
  • 用户可以使用JavaBean将功能、处理、值、数据库访问和其他任何可以用Java代码创造的对象进行打包,并且其他的开发者可以通过内部的JSP页面、Servlet、其他JavaBean、applet程序或者应用来使用这些对象。用户可以认为JavaBean提供了一种随时随地的复制和粘贴的功能,而不用关心任何改变。
  • 《Think in Java》中提到,JavaBean最初是为Java GUI的可视化编程实现的。你拖动IDE构建工具创建一个GUI 组件(如多选框),其实是工具给你创建Java类,并提供将类的属性暴露出来给你修改调整,将事件监听器暴露出来。
     public class JavaBean {
        private String name; // 属性一般定义为private
        private int age;
        public JavaBean() {
        }
        public int getAge() {
            return age;
        }
        public void setAge(int a) {
            age = a;
        }
        public String getName() {
            return name;
        }
        public void setName(String n) {
            name = n;
        }
    }

    11.3 UML类图

    UML(Unified Modeling Language,统一建模语言),用来描述软件模型架构的图形化语言。

  • 常用的UML工具软件有PowerDesingerRoseEnterprise Architect
  • UML工具软件不仅可以绘制软件开发中所需的各种图表,还可以生成对应的源代码。
  • 在软件开发中,使用UML类图可以更加直观地描述类内部结构(类的属性和操作)以及类之间的关系(如关联、依赖、聚合等)。
    • +表示 public 类型, - 表示 private 类型,#表示protected类型
    • 方法的写法:  方法的类型(+、-) 方法名(参数名: 参数类型):返回值类型
    • 斜体表示抽象方法或类。

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值