Java基础知识-第4章:JAVA面向对象解析(上)

215 阅读25分钟

1、初识面向对象

1.1、面向对象的两个要素

:类是一个模板,它描述一类对象的行为和状态 ,是抽象的、概念上的定义。对象:是实际存在的该类事物的每个个体,因而也称为类实例(instance)。下图中汽车类(class) ,而具体的每辆车为该汽车类的对象(object) ,对象包含了汽车的颜色、品牌、名称属性等。

img

面向对象程序设计的重点是类的设计。设计类,就是设计类的成员

  • 属性 = 成员变量 = field = 域、字段
  • 方法 = 成员方法 = 函数 = method
  • 创建类的对象 = 类的实例化 = 实例化类

Java里面,万事万物皆对象

1.2、面向对象基本使用

面向对象程序设计的基本流程:

  • 创建类,设计类的成员
  • 创建类的对象
  • 通过 对象.属性对象.方法调用对象的结构

如果创建了一个类的多个对象,则每个对象都独立的拥有一套类的属性。(非static的属性)意味着:如果我们修改一个对象的这个非static 的属性a,则不影响另外一个对象属性a的值。除非是引用传递(没有new创建新对象)

package com.lanmeix.java;
//测试类
public class PersonTest {
    public static void main(String[] args) {
        
        //2. 创建Person类的对象
        Person p1 = new Person();
        //Scanner scanner = new Scanner(System.in);
        
        //调用对象的结构:属性、方法
        //调用属性:“对象.属性”
        p1.name = "Tom";
        p1.isMale = true;
        System.out.println(p1.name);
        
        //调用方法:“对象.方法”
        p1.eat();
        p1.sleep();
        p1.talk("Chinese");
        
        //*******************************
        Person p2 = new Person();
        System.out.println(p2.name);//默认值null
        System.out.println(p2.isMale);
        
        
        //*******************************
        //引用传递,将p1变量保存的对象地址值赋给p3,导致p1和p3指向了堆空间中的同一个对象实体。
        Person p3 = p1;   
        System.out.println(p3.name);//Tom
        
        p3.age = 10;
        System.out.println(p1.age);//10
        
    }
}
​
//1.创建类,设计类的成员
class Person{
    
    //属性
    String name;
    int age = 1;
    boolean isMale;
    
    //方法
    public void eat(){
        System.out.println("人可以吃饭");
    }
    
    public void sleep(){
        System.out.println("人可以睡觉");
    }
    
    public void talk(String language){
        System.out.println("人可以说话,使用的是:" + language);
    }
}

2、类中属性的使用

2.1、类中变量定义

一个类可以包含以下类型变量:

  • 局部变量:在方法、构造方法或者语句块中定义的变量被称为局部变量。变量声明和初始化都是在方法中,方法结束后,变量就会自动销毁。
  • 成员变量:成员变量是定义在类中,方法体之外的变量。这种变量在创建对象的时候实例化。成员变量可以被类中方法、构造方法和特定类的语句块访问。
  • 类变量:类变量也声明在类中,方法体之外,但必须声明为 static 类型。该变量为多个对象所共享

2.2、对象属性(成员变量) vs 局部变量

相同点:

  • 定义变量的格式:数据类型 变量名 = 变量值
  • 先声明,后使用
  • 变量都有其对应的作用域

不同点:

成员变量局部变量
声明的位置直接定义在类的一对{}声明在方法内、方法形参、代码块内、构造器形参、构造器内部的变量
权限修饰符privatepublic缺省protected不可以使用权限修饰符,可以使用 final 修饰
初始化值根据其类型,都有默认初始化值没有默认初始化值。意味着,我们在调用局部变量之前,一定要显式赋值。特别地:方法的形参在调用时,我们使用实参赋值即可。
内存加载位置堆空间或静态域内栈空间

实例演示

package com.lanmeix.java;
​
public class UserTest {
    
    public static void main(String[] args) {
        User u1 = new User();
        System.out.println(u1.name);
        System.out.println(u1.age);
        System.out.println(u1.isMale);
        
        u1.talk("韩语");
        
        u1.eat();
        
    }
}
​
class User{
    //属性(或成员变量)
    String name;
    public int age;
    boolean isMale;
    
    
    public void talk(String language){//language:形参,也是局部变量
        System.out.println("我们使用" + language + "进行交流");
        
    }
    
    public void eat(){
        String food = "烙饼";//局部变量,必须要显示的赋值
        System.out.println("北方人喜欢吃:" + food);
    }
    
}

2.3、变量作用域

变量的范围是程序中该变量可以被引用的部分,方法内定义的变量被称为局部变量。

  • 局部变量的作用范围从声明开始,直到包含它的块结束。
  • 局部变量必须声明才可以使用。
  • 方法的参数范围涵盖整个方法。参数实际上是一个局部变量。

for循环的初始化部分声明的变量,其作用范围在整个循环。但循环体内声明的变量其适用范围是从它声明到循环体结束。它包含如下所示的变量声明:

image.png

你可以在一个方法里,不同的非嵌套块中多次声明一个具有相同的名称局部变量,但你不能在嵌套块内两次声明局部变量。

3、类中方法的使用

3.1、setget方法

在Java中,set方法和get方法通常用于类的属性的访问和修改。它们属于封装的范畴,是面向对象编程的核心概念之一。封装允许将对象的内部状态(即属性)隐藏起来,只通过公开的方法来对其进行访问。这不仅能保证数据的安全性,还能提高代码的可维护性。

  • get方法的作用是返回类的某个属性的值。通常,get方法的命名规则是:get + 属性名(首字母大写)。
  • set方法的作用是为类的属性赋值。通常,set方法的命名规则是:set + 属性名(首字母大写)。
public class Person {
    // 属性
    private String name;
    private int age;
    private String sex;
​
    // 空参构造器
    public Person() {
    }
​
    // 全参构造器
    public Person(String name, int age, String sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
    }
​
    // Getter 和 Setter 方法
    // 获取 name
    public String getName() {
        return name;
    }
​
    // 设置 name
    public void setName(String name) {
        this.name = name;
    }
​
    // 获取 age
    public int getAge() {
        return age;
    }
​
    // 设置 age
    public void setAge(int age) {
        this.age = age;
    }
​
    // 获取 sex
    public String getSex() {
        return sex;
    }
​
    // 设置 sex
    public void setSex(String sex) {
        this.sex = sex;
    }
​
    // 可以重写 toString() 方法来输出对象的属性
    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + ", sex=" + sex + "]";
    }
}
​

3.2、类中的方法声明和基本使用

方法:描述类应该具有的功能

无返回值有返回值
无形参void 方法名(){}返回值类型 void 方法名(){}
有形参void 方法名(形参类型 形参){}返回值类型 void 方法名(){}

举例:

public void eat(){}
public void sleep(int hour){}
public String getName(){}
public String getNation(String nation){}

方法的声明

权限修饰符  返回值类型  方法名(形参列表){  
    方法体  
} 

注意:staticfinalabstract 来修饰的方法,后面再讲。

方法的命名规则

  • 方法的名字的第一个单词应以小写字母作为开头,后面的单词则用大写字母开头写,不使用连接符。例如:addPerson
  • 下划线可能出现在 JUnit 测试方法名称中用以分隔名称的逻辑组件。一个典型的模式是:test<MethodUnderTest>_<state>,例如 testPop_emptyStack。

说明:

  • 权限修饰符:默认方法的权限修饰符先都使用public ,Java规定的4种权限修饰符:private、public、缺省、protected -->封装性再细说

  • 返回值类型

    • 如果方法有返回值,则必须在方法声明时,指定返回值的类型。同时,方法中,需要使用return关键字来返回指定类型的变量或常量:return 数据
    • 如果方法没有返回值,则方法声明时,使用void来表示。通常,没有返回值的方法中,就不需要使用return。但是,如果使用的话,只能return; 表示结束此方法的意思。
  • 方法名:属于标识符,遵循标识符的规则和规范,见名知意

  • 方法体:方法功能的体现。

方法调用:方法分为静态方法和非静态方法

  • 非静态-实例方法可以通过对象.方法调用
  • 静态方法可以通过类名.方法调用

方法的使用细节注意:

  • 方法的使用中,可以调用当前类的属性或方法,其实省略了this。
  • 特殊的:方法A中又调用了方法A,递归方法
  • 方法中,不可以定义方法。
class Customer{   
​
    //属性  
    String name;  
    int age;  
    boolean isMale;  
​
    //方法  
    public void sleep(int hour){  
        System.out.println("休息了" + hour + "个小时");         
        eat();  //方法中可以调用当前类的方法,其实省略了this
        //sleep(10);  //递归方法
​
    }  
​
    public String getName(){          
        if(age > 18){  //方法中可以调用当前类的属性age,省略了this
            return name;              
        }else{  
            return "Tom";  
        }  
    }     

3.3、方法的重载(overload)

定义: 在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或者参数类型不同即可。

  • 同一个类、相同方法名
  • 参数列表不同:参数个数不同,参数类型不同

举例: Arrays类中重载的binarySearch()方法

image.png

重载特点: 跟方法的权限修饰符、返回值类型、形参变量名、方法体都没有关系!只看方法名和方法参数列表的不同

package com.lanmeix.java1;
​
public class OverLoadTest {
    public static void main(String[] args) {
        OverLoadTest test = new OverLoadTest();
        test.getSum(1,2);//会优先选择第一个getSum方法,精确匹配优先
    }
    
    //如下的4个方法构成了重载
    public void getSum(int i,int j){
        System.out.println("1");
    }
    
    public void getSum(double d1,double d2){
        System.out.println("2");
    }
    
    public void getSum(String s ,int i){
        System.out.println("3");
    }
    
    public void getSum(int i,String s){
        System.out.println("4");
    }
}

如下的3个方法不能与上述4个方法构成重载

public int getSum(int i,int j){
    return 0;
}
​
public void getSum(int m,int n){}
private void getSum(int i,int j){}

3.4、新特性:可变个数的形参

可变个数形参的方法:是jdk5.0新增的内容

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

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

具体使用:

  • 可变个数形参的格式:数据类型 ... 变量名

  • 当调用可变个数形参的方法时,传入的参数个数可以是:0个,1个,2个,。。。

  • 可变个数形参的方法与本类中方法名相同,形参不同的方法之间构成重载

  • 可变个数形参的方法与本类中方法名相同,形参类型也相同的数组之间不构成重载。换句话说,二者不能共存。

public void show(String ... strs){
    System.out.println("show(String ... strs)");

    for(int i = 0;i < strs.length;i++){
        System.out.println(strs[i]);
    }
}
//不能与上一个方法同时存在,不构成重载
public void show(String[] strs){}
  • 可变个数形参在方法的形参中,必须声明在末尾,防止参数类型冲突
//The variable argument type String of the method 
//show must be the last parameter
public void show(String ...strs,int i){}
  • 可变个数形参在方法的形参中,最多只能声明一个可变形参

实例演示:

public class MethodArgsTest {
	public static void main(String[] args) {	
		MethodArgsTest test = new MethodArgsTest();
		test.show(12);
		test.show("hello");
		test.show("hello","world");
		test.show();		
		test.show(new String[]{"AA","BB","CC"}); //这样是正确的,相当于jdk5.0以前的写法
	}
	
	public void show(int i){}
	
	public void show(String s){
		System.out.println("show(String)");
	}
	
	public void show(String ... strs){
		System.out.println("show(String ... strs)");	
		for(int i = 0;i < strs.length;i++){
			System.out.println(strs[i]);
		}
	}
}

3.5、方法参数的值传递机制

方法,必须由其所在类或对象调用才有意义。若方法含有参数:

  • 形参:方法声明时的参数
  • 实参: 方法调用时实际传给形参的参数值

Java的实参值如何传入方法呢?

  • Java里方法的参数传递方式只有一种:值传递。 即将实际参数值的副本(复制品)传入方法内,而参数本身不受影响
  • 形参是基本数据类型:将实参基本数据类型变量的“数据值”传递给形参
  • 形参是引用数据类型:将实参引用数据类型变量的“地址值”传递给形参

引用传递--针对于方法内变量的赋值举例

  • 如果变量是基本数据类型,此时赋值的是变量所保存的数据值。
  • 如果变量是引用数据类型,此时赋值的是变量所保存的数据的地址值。
public class ValueTransferTest {
    public static void main(String[] args) {
        System.out.println("***********基本数据类型:****************");
        int m = 10;
        int n = m;

        System.out.println("m = " + m + ", n = " + n);
        n = 20;
        System.out.println("m = " + m + ", n = " + n);

        System.out.println("***********引用数据类型:****************");

        Order o1 = new Order();
        o1.orderId = 1001;

        Order o2 = o1; //赋值以后,o1和o2的地址值相同,都指向了堆空间中同一个对象实体。

        System.out.println("o1.orderId = " + o1.orderId + ",o2.orderId = " +o2.orderId);
        o2.orderId = 1002;	
        System.out.println("o1.orderId = " + o1.orderId + ",o2.orderId = " +o2.orderId);
    }
}

class Order{	
    int orderId;	
}

参数传递机制:值传递-----针对于方法的参数概念

  • 形参:方法定义时,声明的小括号内的参数
  • 实参:方法调用时,实际传递给形参的数据

值传递机制实例:针对基本数据类型

/*
 * 方法的形参的传递机制:值传递
 */
public class ValueTransferTest1 {
    public static void main(String[] args) {
        int m = 10;
        int n = 20;

        System.out.println("m = " + m + ", n = " + n);

        ValueTransferTest1 test = new ValueTransferTest1();
        test.swap(m, n);
        System.out.println("m = " + m + ", n = " + n);	//m=10,n=20,没有发生交换
    }
		
    public void swap(int m,int n){
        int temp = m ;
        m = n;
        n = temp;
    }
}

理解:swap方法执行完以后,里面的变量就销毁回收,所以然后main方法打印的还是原来的值,可以延伸在数组元素的交换

值传递机制实例:针对引用数据类型

public class ValueTransferTest2 {
    public static void main(String[] args) {
        Data data = new Data();
        data.m = 10;
        data.n = 20;

        System.out.println("m = " + data.m + ", n = " + data.n);

        ValueTransferTest2 test = new ValueTransferTest2();
        test.swap(data); //传入引用数据类型地址值

        System.out.println("m = " + data.m + ", n = " + data.n);//发生了交换	
    }

    public void swap(Data data){
        int temp = data.m;
        data.m = data.n;
        data.n = temp;
    }	
}

class Data{	
	int m;
	int n;	
}

3.6、递归方法的使用

递归方法:一个方法体内调用它自身。 如何理解递归方法?

  • 方法递归包含了一种隐式的循环,它会重复执行某段代码,但这种重复执行无须循环控制。
  • 递归一定要向已知方向递归,否则这种递归就变成了无穷递归,类似于死循环。

例1:计算1-100之间所有自然数的和

public class RecursionTest {

    public static void main(String[] args) {
        // 方式一:
        int sum = 0;
        for (int i = 1; i <= 100; i++) {
             sum += i;
        }
        System.out.println(sum);

        // 方式二:递归
        RecursionTest test = new RecursionTest();
        int sum1 = test.getSum(100);
        System.out.println(sum1);
    }

    //计算1-n之间所有自然数的和
    public int getSum(int n) {
        if (n == 1) {
            return 1;
        }else {
            return n + getSum(n - 1);
        }
    }
}

例2:计算1-n之间所有自然数的乘积:n!

// 例2:计算1-n之间所有自然数的乘积:n!
public int getSum1(int n) {
    if (n == 1) {
        return 1;
    } else {
        return n * getSum1(n - 1);
    }
}

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

public class RecursionTest {
    public static void main(String[] args) {
        int value = test.f(10);
        System.out.println(value);
    }

    //例3:已知有一个数列:f(0) = 1,f(1) = 4,f(n+2)=2*f(n+1) + f(n),
    //其中n是大于0的整数,求f(10)的值。
    public int f(int n){
        if(n == 0){
            return 1;
        }else if(n == 1){
            return 4;
        }else{
            //return f(n + 2) - 2 * f(n + 1);//会内存溢出
            return 2*f(n - 1) + f(n - 2);
        }
    }
}

其他:

  • 斐波那契数列
  • 汉诺塔问题
  • 快排

3.7、return关键字

使用范围:使用在方法体中

作用

  • 结束方法
  • 针对于有返回值类型的方法,使用"return 数据"方法返回所要的数据。

注意点return关键字后面不可以声明执行语句。

public void eat(){  
    System.out.println("客户吃饭");  
    return;  
    //return后不可以声明表达式  
    //System.out.println("hello");  
}  

4、构造器Constructor

构造器的作用:

  • 创建对象
  • 初始化对象属性的信息
  • 初始化其他一些自定义信息

使用说明:

  • 如果没显式的定义类的构造器的话,则系统默认提供一个空参的构造器
  • 定义构造器的格式:权限修饰符 类名(形参列表){}
  • 一个类中定义的多个构造器,彼此构成重载
  • 一旦我们显式的定义了类的构造器之后,系统就不再提供默认的空参构造器,但是我们可以显示的将空参构造器写出来
  • 一个类中,至少会有一个构造器(空参)。

举例:

public class Person {
    // 属性
    private String name;
    private int age;
    private String sex;

    // 空参构造器
    public Person() {
    }

    // 全参构造器
    public Person(String name, int age, String sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
    }
}

5、万事万物皆对象

在Java语言范畴中,我们都将功能、结构等封装到类中,通过类的实例化,来调用具体的功能结构。涉及到Java语言与前端Html、后端的数据库交互时,前后端的结构在Java层面交互时,都体现为类、对象。例如,表就是一个对象、文件File、网络资源URL

内存解析的说明

  • 引用类型的变量,只可能存储两类值:null 或 地址值(含变量的类型)
  • 自定义类是引用数据类型,所以student数组里面存放的是学生对象的地址值或null

JVM内存解析简述

编译完 java 源程序以后,生成一个或多个字节码文件(.class)。然后,JVM中类的加载器和解释器会对生成的字节码文件进行解释运行。意味着,需要将字节码文件对应的类加载到内存中,涉及到内存解析。

《JVM规范》

  1. 虚拟机栈:即为平时提到的栈结构。我们将局部变量存储在栈结构中。通常所说的 栈 Stack 就是指虚拟机栈 。 虚拟机栈用于存储局部变量等 。局部变量表存放了编译期可知长度的各种基本数据类型,如:boolean、byte、char、short、int、float、long、double、对象引用(reference类型它不等同于对象本身,是对象在堆内存的首地址<对象名字和地址>) 。方法执行完自动释放变量。(方法里面的变量都叫局部变量)
  2. :我们将new出来的结构(比如:数组、对象)加载在堆空间中。此外,对象的属性(非static的)也是加载在堆空间中。此内存区域的唯一目的就是:存放对象实例,几乎所有的对象实例 (new出来的结构,属性)都在这里分配内存。这一点在Java 虚拟机规范中的描述是:所有的对象实例以及数组都要在堆上分配。
  3. 方法区:用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

5.1、对象的内存解析简述

image.png

【案例】

image.png

5.2、String类型的内存解析

字符串类型不同于其他引用数据类型,需要特殊说明,

public class ValueTransferTest {
    public static void main(String[] args) {
        String s1 = "hello";
        ValueTransferTest test = new ValueTransferTest();
        test.change(s1);

        //值传递,方法里面的形参s只不过是一个拷贝的副本,可以看做是s2,只是两者都指向hello罢了
        System.out.println(s1);//hello	
    }

    public void change(String s){
        s = "hi~~"; //只能新造一个,所以s指向的是另外一个hello,然后将hello改成hi,并且指向hi
    }
}

之所以 System.out.println(s1);//hello 是这样的。是因为string类型字符串会保存在方法区的常量池,方法区不可更改,更改只能新造一个,所以当change方法执行完毕,hi回收,常量池就只剩下s1指向的hello了。

6、匿名对象

我们创建的对象,没有显式的赋给一个变量名。即为匿名对象

  • 特征:匿名对象只能调用一次。
public class InstanceTest {
    public static void main(String[] args) {

        //匿名对象		
        new Phone().price = 1999;
        new Phone().showPrice();//0.0

        //***匿名对象的传参**********************
        PhoneMall mall = new PhoneMall();
        mall.show(new Phone());		
    }
}

class PhoneMall{	
    public void show(Phone phone){
        phone.sendEmail();
        phone.playGame();
    }	
}

class Phone{
    double price;//价格

    public void sendEmail(){
        System.out.println("发送邮件");
    }

    public void playGame(){
        System.out.println("玩游戏");
    }

    public void showPrice(){
        System.out.println("手机价格为:" + price);
    }	
}

7、面向对象的特征一:封装与隐藏

为什么要引入封装性?

  • 我们程序设计追求 “高内聚,低耦合”
  • 高内聚:类的内部数据操作细节自己完成,不允许外部干涉;
  • 低耦合:仅对外暴露少量的方法用于使用。
  • 隐藏对象内部的复杂性,只对外公开简单的接口。便于外界调用,从而提高系统的可扩展性、可维护性。通俗的说,把该隐藏的隐藏起来,该暴露的暴露出来。这就是封装性的设计思想。

问题引入

  • 当我们创建一个类的对象以后,我们可以通过"对象.属性"的方式,对对象的属性进行赋值。这里,赋值操作要受到属性的数据类型和存储范围的制约。除此之外,没其他制约条件。但是,在实际问题中,我们往往需要给属性赋值加入额外的限制条件。这个条件就不能在属性声明时体现,我们只能通过方法进行限制条件的添加。(比如:对某个属性进行setter操作的同时,我们需要避免用户再使用"对象.属性"的方式对属性进行赋值。则需要将属性声明为私有的(private),此时,针对于属性就体现了封装性

封装性思想具体的代码体现:

  • 体现1:将类的属性私有化(private),这样只能本类才能访问,其他类都访问不了,如此就对信息进行了隐藏。 同时,提供公共的public方法来获取(getXxx)和设置(setXxx)此私有属性的值,当构造器出现后,可以进行属性的初始化,并不意味着set方法没用了,因为属性私有化,set方法可以起到在外面在通过对象设置属性值的作用。通过setget方法,属性被封装起来,类的外部无法直接访问和修改属性值,只有通过方法进行。
//属性私有化,就不能通过在外部通过 对象.属性 方式进行属性的获取和修改
private double radius;  

//生成公共的set/get方法,可以在外部通过 对象.方法 方式进行属性的获取和修改
public void setRadius(double radius){  
    this.radius = radius;  
}  
public double getRadius(){  
    return radius;  
} 
  • 体现2:增加灵活性。在set方法中,你可以添加额外的验证逻辑,确保属性值的合法性。getset方法为后期的维护和扩展提供了灵活性。如果直接访问字段,代码就不容易维护。通过方法,你可以随时改变方法内部的实现,而不影响使用它们的代码。
public void setAge(int age) {
    if (age > 0) {
        this.age = age;
    } else {
        System.out.println("年龄不能小于0!");
    }
}
  • 体现3:不对外暴露的私有的方法
  • 体现4:单例模式(将构造器私有化)
  • 体现5:如果不希望类在当前所在的外被调用,可以将类设置为缺省的。

封装性的体现,需要权限修饰符来配合,Java规定的四种权限修饰符

  • 权限范围从小到大顺序为:private < 缺省 < protected < public
  • Java权限修饰符置于类的成员定义前,用来限定对象对该类成员的访问权限。4种权限都可以用来修饰类的内部结构:属性、方法、构造器、内部类
  • 对于class的权限修饰只可以用 public和 default
    • public 类可以在任意地方被访问。
    • default类只可以被同一个包内部的类访问。
  • 具体的修饰范围:

image.png

8、属性赋值情况说明

总结:前面提到了很多变量赋值情况,通过set方法,构造器等,因此有一个属性赋值的先后顺序

  • ① 默认初始化值
  • ② 显式初始化
  • ③ 构造器中初始化
  • ④ 通过"对象.方法""对象.属性" 的方式,赋值
  • 以上操作的先后顺序:① - ② - ③ - ④
package com.lanmeix.java1;
public class UserTest {
    public static void main(String[] args) {
        User u = new User();

        System.out.println(u.age);

        User u1 = new User(2);

        u1.setAge(3);
        u1.setAge(5);

        System.out.println(u1.age);
    }
}

class User{
    String name; //默认初始化值null
    int age = 1;  //显示初始化age=1

    public User(){		
    }

    public User(int a){ //构造器中初始化
        age = a;
    }

    public void setAge(int a){ //set方法为属性赋值
        age = a;
    }	
}

9、基本关键字

9.1、this

定义

  • 在 Java 中, this 关键字比较难理解,它的作用和其词义很接近。
  • 它在方法内部使用,即这个方法所属对象的引用
  • 它在构造器内部使用,表示该构造器正在初始化的对象
  • this可以调用的结构:属性、方法;构造器

什么时候使用this关键字呢?

  • 当在方法内需要用到调用该方法的对象时,就用 this
  • 具体的:当方法或构造器形参和属性名相同时,我们可以用 this 来区分属性和局部变量
class Person{

    private String name;
    private int age;

    public Person(String name){
        this.name = name;//区分属性和局部变量,this可以省略
    }

    public void setName(String name){
        this.name = name; //区分属性和局部变量,this可以省略
    }
    public String getName(){
        return this.name;
    }
}	

this 调用方法:

  • this 理解为:当前对象 或 当前正在创建的对象
  • 在类的方法或构造器中,我们可以使用 this.属性this.方法的方式,调用当前对象属性或方法。但是,通常情况下,我们都择省略this。特殊情况下,如果方法的形参和类的属性同名时,我们必须显式的使用this.变量的方式,表明此变量是属性,而非形参。
  • 使用 this 访问属性和方法时,如果在本类中未找到,会从父类中查找
class Person{

    private String name;
    private int age;

    public Person(){		
        this.eat(); //this调用方法eat
        String info = "Person初始化时,需要考虑如下的1,2,3,4...(共40行代码)";
        System.out.println(info);
    }

    public void eat(){
        System.out.println("人吃饭");
        System.out.println("Person 类 ----> " + this .name) ;
        this.study(); //this调用方法study,当前调用 study 方法的this是当前 Person对象
    }
    
    public void study(){
        System.out.println("人学习");
    }
}

this调用构造器

  • 我们在类的构造器中,可以显式的使用 this(形参列表)方式,调用本类中指定的其他构造器
  • 构造器中不能通过this(形参列表)方式调用自己
  • 如果一个类中有n个构造器,则最多有 n-1 构造器中使用了this(形参列表)
  • 规定:this(形参列表)必须声明在当前构造器的首行
  • 构造器内部,最多只能声明一个this(形参列表),用来调用其他的构造器
class Person{

    private String name;
    private int age;
    
    public Person(){  // 无参构造器
        System.out.println("新对象实例化 ")
    }
    
    public Person(String name){
        this();   // 调用本类中的无参构造器
        this.name = name ;
    }
    
    public Person(String name,int age){
        this(name) ;  //调用有一个参数的构造器
        this.age=age
    }
    
    public String getInfo(){
        return "姓名: :" + name + ",年龄 :" + age   
    }
}

9.2、package

使用说明:

  • 为了更好的实现项目中类的管理,提供包的概念
  • 使用package声明类或接口所属的包,声明在源文件的首行
  • 包,属于标识符,遵循标识符的命名规则、规范(xxxyyyzzz)、“见名知意”
  • .一次,就代表一层文件目录。

包的作用:

  • 包帮助管理大型软件系统将功能相近的类或接口划分到同一个包中 。 比如 MVC 的设计模式
  • 包可以包含类和 子包 划分项目层次,便于管理
  • 解决类命名冲突的问题:如同文件夹一样,包也采用了树形目录的存储方式。同一个包中的类名字是不同的,不同的包中的类的名字是可以相同的,当同时调用两个不同包中相同类名的类时,应该加上包名加以区别。因此,包可以避免名字冲突。
  • 控制访问权限:包也限定了访问权限,拥有包访问权限的类才能访问某个包中的类。

Java 使用包(package)这种机制是为了防止命名冲突,访问控制,提供搜索和定位类(class)、接口、枚举(enumerations)和注释(annotation)等。

包语句的语法格式为:

package pkg1[.pkg2[.pkg3…]];

9.3、import

为使用定义在不同包中的 Java 类,需用 import 语句来引入指定包层次下所需要的类或全部类 。 import 语句告诉编译器到哪里去寻找类。

语法格式:

import 包名.类名 //import:导入

如果在一个包中,一个类想要使用本包中的另一个类,那么该包名可以省略。

注意:

  • 在源文件中显式的使用 import 结构导入指定包下的类、接口
  • 声明在包的声明和类的声明之间
  • 如果需要导入多个结构,则并列写出即可
  • 可以使用xxx.*的方式,表示可以导入xxx包下的所结构
  • 如果使用的类或接口是java.lang包下定义的,则可以省略import结构
  • 如果使用的类或接口是本包下定义的,则可以省略import结构
  • 如果在源文件中,使用了不同包下的同名的类,则必须至少一个类需要以全类名的方式显示。
  • 使用xxx.*方式表明可以调用xxx包下的所结构。但是如果使用的是xxx子包下的结构,则仍需要显式导入
  • import static: 导入指定类或接口中的静态结构:属性或方法。

10、JavaBean

JavaBean 是一种 Java 语言写成的可重用组件。所谓JavaBean,是指符合如下标准的 Java 类:

  • 类是公共的
  • 一个无参的公共的构造器
  • 属性,且对应的getset方法
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;
    }
}

如果没有显示的声明构造器,则默认构造器和类的权限一样

用户可以使用 JavaBean 将功能、处理、值、数据库访问和其他任何可以用 Java 代码创造的对象进行打包,并且其他的开发者可以通过内部的 JSP页面、Servlet 、其他 JavaBean、applet 程序或者应用来使用这些对象。用户可以认为 JavaBean 提供了一种随时随地的复制和粘贴的功能,而不用关心任何改变。

11、UML类图

image.png

  • + 表示 public 类型, - 表示 private 类型,# 表示protected类型
  • 方法的写法:方法的类型(+、-) 方法名(参数名: 参数类型):返回值类型