anonymous的Java学习笔记(5)——Java面向对象之类与对象

[toc]

面向对象

Java是面向对象的程序设计语言, Java 语言提供了定义类、成员变量、方法等最基本的功能 。

类与对象

所有使用类定义的变量都是引用变量,它们将会引用到类的对象。也就是说,所有类是引用类型。

定义类

面向对象的程序设计过程中有两个重要概念:类(class)和对象(object,也被称为实例,instance),其中类是某一批对象的抽象,可以把类理解成某种概念。对象才是一个具体存在的实体,从这个意义上来看,日常所说的人,其实都是人的实例,而不是人类。

定义类语法格式

[修饰符] class 类名
{
    零个到多个构造器定义...
    零个到多个成员变量...
    零个到多个方法...
}

在上边的语法格式中

  • 修饰符可以是publicfinalabstract,或者完全省略这三个修饰符
  • 类名命名规则遵循大驼峰命名法规则:每个单词首字母大写,其他字母全部小写,单词与单词之间不要使用任何分隔符。
    • 例如
    MyTest、HandleExcel、HandleRequests
    

类的组成

一个类通常包含三中最常见的成员。

  • 构造器
    • 构造器是一个类创建对象的根本途径,如果一个类没有构造器,这个类通常无法创建实例。
    • 因此,Java语言提供了一个功能:如果程序员没有为一个类编写构造器,则系统会为该类提供一个默认的构造器。一旦程序员为一个类提供了构造器,系统将不再为该类提供构造器。
  • 成员变量:成员变量用于定义该类或该类的实例所包含的状态数据。
  • 方法:方法则用于定义该类或该类的实例的行为特征或者功能实现。

需要注意的点

  1. 类中的成员数量无限制。
  2. 类中各成员之间的定义顺序没有要求,各成员之间可以互相调用。但是static修饰的成员不能访问没有static修饰的成员。

成员变量

定义成员变量语法格式

[修饰符] 类型 成员变量名 [=默认值];

成员变量详细说明

  • 修饰符
    • 可以省略
    • 可以是publicprotectedprivatestaticfinal,其中publicprotectedprivate三个最多只能出现一个,可以与staticfinal组合使用来修饰成员变量
  • 类型
    • 不可省略,可以是基本数据类型和引用数据类型。
  • 成员变量名
    • 遵循小驼峰命名法规则:第一个单词首字母小写,后面每个单词首字母大写,其他字母全部小写,单词与单词之间不要使用任何分隔符。
  • 默认值
    • 定义成员变量还可以指定 个可边的默认值

方法

定义方法语法格式

[修饰符] 方法返回值类型 方法名(形参列表)
{
    //由零条或多条可执行语句组成的方法体
}

方法详细说明

  • 修饰符
    • 可以省略
    • 可以是publicprotectedprivatestaticfinal,其中publicprotectedprivate三个最多只能出现一个,可以与staticfinal组合使用来修饰方法
  • 方法返回值类型
    • 返回值类型可以是基本数据类型和引用类型
    • 如果声明了方法返回值类型,方法体内必须提供一个return语句,返回一个变量或表达式。变量或者表达式的类型必须与此处声明的类型匹配。
    • 如果未声明方法返回值类型,必须使用void声明该方法没有返回值。
  • 方法名
    • 方法名命名规则与成员变量命名规则一致。
  • 形参列表
    • 形参列表用于定义该方法可以接受的参数
    • 格式:由零组或多组形参组成
      • 零组就是值该方法没有形参
      • 一组形参:(数据类型 形参名)
      • 多组形参:(数据类型 形参名1, 数据类型 形参名2)
    • 一旦定义方法时指定了形参列表,调用方法时必须传入对应的参数值(谁调用方法,谁负责为形参赋值)
  • 方法体
    • 方法体里多条可执行性语句之间有严格的执行顺序,排在方法体前面的语句总是先执行,排在方法体后面的语句总是后执行。

static关键字

static的真正作用就是用于区分成员变量、方法、内部类、初始化块这四种成员到底属于类本身还是属于实例。

在类中定义的成员,static相当于一个标志,有static修饰的成员属于类本身,没有static修饰的成员属于该类的实例。

  • 通常把static修饰的成员变量和方法也称为类变量、类方法。
  • 不使用static修饰的普通方法、成员变量则属于该类的单个实例,而不属于该类。因此通常把不使用static修饰的成员变量和方法也称为实例变量、实例方法。

构造器

构造器是一个特殊的方法,定义构造器的语法格式与定义方法的语法格式很像,定义构造器的语法格式如下:

[修饰符] 构造器名 (形参列表)
{
    //由零条到多条可执行性语句组成的构造器执行体
}

构造器详细说明

  • 修饰符
    • 可以省略
    • 可以是publicprotectedprivate其中之一
  • 构造器名字:构造器名字必须与类名相同。
  • 形参列表:和定义方法形参列表的格式完全相同。
    使用构造器时需要注意的点
  • 构造器既不能定义返回值类型,也不能使用void声明构造器没有返回值。
  • 如果为构造器定义了返回值类型,或使用void声明构造器没有返回值,编译时不会出错,但Java会把这个所谓的构造器当成方法来处理——>它就不再是构造器。

构造器不是没有返回值吗,为什么不能用void来声明呢?

  1. 这是Java语法的规定
  2. 实际上,类的构造器是有返回值的。当使用new关键字来调用构造器时,构造器返回该类的实例,可以把这个类的实例当成构造器的返回值,因此构造器的返回值类型总是当前类,无需定义返回值类型。
  3. 必须注意:不要在构造器里显式使用return来返回当前类的对象,因为构造器的返回值是隐式的。

定义一个Person类

public class Person{
    //定义两个成员变量
    public String name;
    public int age;
    //定义一个say方法
    public void say(String content){
        System.out.println(content);
    }
}

上边的Person类里没有手动定义构造器,系统自动为该类提供一个构造器,系统提供的构造器总是没有参数的。

Java中类的作用

  • 定义变量
  • 创建对象
  • 调用类的类方法或访问类的类变量。

使用类

对象的产生和使用

创建对象的根本途径是构造器,通过new关键字来调用某个类的构造器即可创建这个类的实例。

创建对象
//使用Person类定义一个Person类型的变量
Person p;
//通过new关键字调用Person类的构造器,返回一个Person实例
//讲该Person实例赋值给p
p = new Person();
//上边代码可以简写成一行代码
Person p = new Person();
使用对象

Java的对象大致有如下作用

  • 访问对象的实例变量(也叫实例属性)
  • 调用对象的方法

注意点

  • 类或实例访问方法或成员变量的语法是:类.类变量|方法,或者实例.实例变量|方法
  • static修饰的方法和成员变量,既可通过类来调用,也可通过实例来调用
  • 没有使用static修饰的普通方法和成员变量,只可通过实例来调用
  • 如果堆内存里的对象没有任何变量指向该对象,那么程序将无法再访问该对象,这个对象也就变了垃圾,Java的垃圾回收机制将回收该对象,释放该对象所占的内存区。
  • 如果希望通知垃圾回收机制回收某个对象,只需要切断该对象的所有引用变量和它之间的关系即可,也就是把这些引用变量赋值为**null**。

代码示例

Person.java

package com.abc.part_four;

public class Person {
    //定义两个成员变量
    public String name;
    public int age;

    //定义一个say方法
    public void say(String content) {
        System.out.println(content);
    }

}

PersonTest.java

package com.abc.part_four;

public class PersonTest {
    public static void main(String[] args) {
        Person p = new Person();
        System.out.println("***************未对Person类的实例属性name,age进行赋值前***************");
        System.out.println(p.name);
        System.out.println(p.age);
        p.name = "小明";
        p.age = 21;
        System.out.println("***************对Person类的实例属性name,age进行赋值后***************");
        System.out.println(p.name);
        System.out.println(p.age);
        System.out.println("***************调用Person类的实例方法say***************");
        p.say("人生苦短,Let's Go!");
    }


}

运行PersonTest.java输出以下结果

***************未对Person类的实例属性name,age进行赋值前***************
null
0
***************对Person类的实例属性name,age进行赋值后***************
小明
21
***************调用Person类的实例方法say***************
人生苦短,Let's Go!

大部分时候,定义一个类就是为了重复创建该类的实例,同一个的多个实例具有相同的特征,而类则是定义了多个实例的共同特征。从某个角度来看,类定义的是多个实例的特征,因此类不是一种具体存在,实例才是具体存在 。完全可以这样说:你不是人这个类,我也不是人这个类,我们都只是人的实例。

对象this的使用

Java中this关键字总是指向调用该方法的对象。this作为对象的默认引用有两种情形:

  1. 构造器中引用该构造器正在初始化的对象。
  2. 在方法中引用调用该方法的对象。
在方法中引用调用该方法的对象

this关键宇最大地作用就是让类中一个方法,访问该类里的另一个方法或实例变量。

代码示例

Dog1.java

package com.abc.part_four;

public class Dog1 {
    public String dogName;

    public void jump() {
        System.out.println("Dog jump~");
    }

    public void run() {
        dogName = "小花花";
        Dog1 dog = new Dog1();
        dog.jump();//假设Dog run的前提是Dog jump
        System.out.println("Dog run~");
    }
}

Dog2.java

package com.abc.part_four;

public class Dog2 {
    public String dogName;

    public void jump() {
        System.out.println("Dog jump~");
    }

    public void run() {
        dogName = "小花花";
        this.jump();//假设Dog run的前提是Dog jump
        System.out.println("Dog run~");
    }
}

Dog3.java

package com.abc.part_four;

public class Dog3 {
    public String dogName;

    public void jump() {
        System.out.println("Dog jump~");
    }

    public void run() {
        dogName = "小花花";
        jump();//假设Dog run的前提是Dog jump
        System.out.println("Dog run~");
    }
}

DogTest.java

package com.abc.part_four;

public class DogTest {
    public static void main(String[] args) {
        System.out.println("*********Dog1*********");
        Dog1 d1 = new Dog1();
        // 调用Dog1里的run方法,创建两个Dog对象
        d1.run();
        /**
         * 这里产生了两个问题
         * 第一个问题:Dog1类中run()方法中调用jum()方法时是否一定需要一个Dog对象?
         * 第二个问题:是否一定需要重新创建一个Dog对象?
         * 第一个问题的答案是肯定的,因为没有使用static修饰的成员变量和方法都必须使用对象来调用 。
         * 第二个问题的答案是否定的,因为当程序调用run()法时,一定会提供一个Dog对象,这样就可以直接使用这个己经存在的Dog对象,而无须重新创建新Dog对象了.
         * 因此需要在run()方法中获得调用该方法的对象,通过this关键字就可以满足这个要求。
         *
         * this可以代表任何对象,当this出现在某个方法体中时,它所代表的对象是不确定的,但它的类型是确定的:它所代表的只能是当前类的实例,只有当这个方法被调用时,它所代表的对象才被确定下来:谁在调用这个方法,this就代表谁。
         * 因此将Dog1中run()方法中使用this关键字来调用jump()方法会更合适些。
         * 代码如Dog2所示
         */
        System.out.println("*********Dog2*********");
        Dog2 d2 = new Dog2();
        //调用Dog1里的run方法,只创建了一个Dog对象
        d2.run();
        /**
         * this关键字可以省略
         * 代码如Dog3所示
         */
        System.out.println("*********Dog3*********");
        Dog3 d3 = new Dog3();
        d3.run();


    }
}

对于static修饰的方法而言,可以使用类来直接调用该方法,static修饰的方法中不能使用this引用。

  • 由于static修饰的方法不能使用this引用,所以static修饰的方法不能访问不使用static修饰的普通成员,因此Java语法规定:静态成员(包括方法、成员变量)不能直接访问非静态成员(包括方法、成员变量)。

静态方法直接访问非静态方法时引发错误代码示例

package com.abc.part_four;

public class StaticAccessNonStatic {
    public void say1(){
        System.out.println("一个简单的非静态方法");
    }
    public static void say2(){
        System.out.println("一个简单的静态方法");
    }
    public static void main(String[] args){
        //执行程序编译时会报错:Error:(11, 9) java: 无法从静态上下文中引用非静态 方法 say1()。
        // 因为say1()是属于示例的方法不是属于类的方法。因此必须需要使用对象来调用该方法。
        //say1();
        /**
         * 在上面的main()方法中直接调用say1()方法时,系统相当于使用this作为该方法的调用者,
         * 而main()方法是static修饰的方法,static修饰的方法属于类,而不属于对象,因此调用static修饰的方法的主调总是类本身.
         */
        //如果确实需要在静态方法里调用非静态方法。可以创建类对象,使用类对象来调用非静态方法。
        new StaticAccessNonStatic().say1();//输出:一个简单的非静态方法
        //执行不会报错
        say2();//输出:一个简单的静态方法
    }
}

大部分时候,普通方法访问其他方法、成员变量时无须使用**this前缀 ,但如果方法里有个局部变量和成员变量同名,但程序又需要在该方法里访问这个被覆盖的成员变量,则必须使用`this1**前缀。

构造器中引用该构造器正在初始化的对象(简单来说就是在构造器中使用this关键字)

this引用也可以用于构造器中作为默认引用,由于构造器是直接使用new关键宇来调用,而不是使用对象来调用的,所以this在构造器中代表该构造器正在初始化的对象。

代码示例

package com.abc.part_four;

public class ThisInConstructor {
    public String name;

    //定义一个构造器
    public ThisInConstructor() {
        //在构造器里定义一个name变量
        String name;
        //使用this代表该构造器正在初始化的对象
        //下面的代码将会把该构造器正在初始化的对象的name成员变量设置成`小花花`
        this.name = "小花花";
    }

    public static void main(String[] args) {
        System.out.println(new ThisInConstructor().name);//小花花
    }
}

程序输出

小花花