首页 > 技术文章 > java面向对象编程

wushuaiqi 2021-10-15 19:38 原文

类和对象:

1-类:
    1.成员变量
    2.成员方法
    3.权限修饰符
    4.局部变量及有效范围
    5.this关键字
2-类的构造方法
3-静态成员:
    1.静态变量
    2.静态方法
4-类的主方法
5-对象:
    1.创建
    2.访问属性和行为
    3.引用
    4.销毁

类——成员变量

在java中,对象的属性也成为成员变量,成员变量可以是任意类型,整个类中均是成员变量的作用范围。
public class Book {
    // String类型的成员变量
    private String name;
    // name的Getter方法
    public String getName(){
        return name;
    }
    // name的Setter方法
   public void SetName (String name){
        // 将参数赋予类中的成员变量
        this.name = name;
   } 
}

在上面的这个实例中可以看到,在java中需要 class 关键字来定义类,Book 是类的名称。同时在Book类中定义了一个成员变量,成员变量的类型为 String 类型,其实成员变量就是普通的变量,可以为它设置初始值,也可以不设置初始值,如果不设置初始值,则会有默认值。

类——成员方法

在java中,使用成员方法对应于类对象的行为。以Book类为例,它包含 getName() 和
setName() 两个方法,这两个成员方法分别为获取图书名称和设置图书名称的方法。

定义成员方法的语法格式如下:

权限修饰符 返回值类型 方法名(参数类型 参数名){
    ...... 方法主体
    return 返回值;
}

一个成员方法可以有参数,这个参数可以是对象,也可以是基本的数据类型的变量,同时成员方法有返回值和无返回值的两种选择,如果方法需要返回值,可以在方法体中使用 return 关键字,使用这个关键字后,方法的执行将被终止。

注意:要使 java 代码中的成员方法无返回值,可以使用 void 关键字表示。

成员方法的放回值可以是计算结果,也可以是其他想要的数值和对象,返回值类型要与方法返回的值类型一致。

在成员方法中可以调用其他成员方法和类成员变量,例如在 Book 类中的 getName() 方法中就调用了 setName() 方法将图书名称赋予一个值。同时,在成员方法中可以定义一个变量,这个变量为局部变量。

说明:如果一个方法中含有与成员变量同名的局部变量,则方法中对这个变量的访问以局部变量进行。
类的成员变量和成员方法也可以统称为类成员。

类——权限修饰符

访问包位置 private protected public
本类 可见 可见 可见
同包其他类或子类 不可见 可见 可见
其他包的类或子类 不可见 不可见 可见
注意:声明类是不使用 public 、 protected 和 private 修饰符设置类的权限,
则这个类预设为包存取范围,即只有一个包中的类可以访问这个类的成员变量和方法。

例如: 在项目中的 com.mr 包下创建 AuyClass 类,该类使用默认的访问权限。

package com.mr;
class AnyClass {
    public void doString(){
        //方法主体
    }
}

在上述代码中,由于类的修饰为默认修饰符,即只有一个包内的其他类和子类可以对该类进行访问,而 AnyClss 类中的 doString() 方法却被设置为 public 访问权限,即使这样, doString() 方法的访问权限已然与 AnyClass 类的访问权限相同。

类——局部变量

如果在成员方法内定义一个变量,那么这个变量被称为局部变量,

实际中,方法中的形参也可以作为一个局部变量,例如 Book 类中定义的 setName(String name) 方法, String name 这个形参就被看作是局部变量。

局部变量在方法执行时创建,在方法执行结束时被销毁。局部变量在使用时必须进行赋值操作或被初始化,否则就会出现编译错误。

例如:在 ChangeDemo 类中创建静态的 exchange() 方法,改方法可以将数组参数 arr 的前两个元素值进行互换,通过在方法中定义一个保存临时数据的局部变量 tmp, 利用 tmp 交换两个元素的值。

public class ChangeDemo {
    public static int[] exChange(int[] arr) {
        int tmp = arr[0];
        arr[0] = arr[1];
        arr[1] = tmp;
        return arr;
    }

    public static void main(String[] args) {
        int[] arr = {17, 29};
        System.out.println("第一个值=" + arr[0] + ",第二个值=" + arr[1]);
        arr = exChange(arr);
        System.out.println("第一个值=" + arr[0] + ",第二个值=" + arr[1]);
    }
}

运行结果:

第一个值=17,第二个值=29
第一个值=29,第二个值=17

注意:在作用范围外使用局部变量是一个常见的错误,应为在作用范围外没有声明局部变量的代码。

this 关键字

this 关键字用于表示本类当前的对象,当前对象不是某个 new 出来的实体对象,而是当前正在编辑的类。 this 关键字只能在本类中使用。

例如 Book 类中的 getName() 方法,代码如下:

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

在上述代码中可以看到,成员变量与 setName() 方法中的形式参数的名称相同,都为 name, 那么该如何在类中区分使用的是哪一个变量?在 java 中,规定使用 this 关键字来代表本类对象的引用, this 关键字被隐式地用于对象的成员变量和方法,在上述代码中, this.name 指的是 Book 类中的 name 成员变量,而 this.name = name 语句中的第二个 name 则指的是形参 name。 实际上,setName() 方法实现的功能就是将形参 name 的值赋予成员变量 name。

事实上,this 引用的就是本类的一个对象,在局部变量或方法参数覆盖了成员变量时,如上面代码的情况,就要添加 this 关键字明确引用的是类成员还是局部变量或方法参数。

this 除了可以调用成员变量或成员方法,还可以作为方法的返回值。例如,返回图书类本类的对象,可以写成下面这种形式:

public  Book getBook() {
    return this;
}

在 getBook() 方法中,返回值为 Book 类,所以方法体中使用 return this 这种形式返回 Book 类对象。

类的构造方法

在类中,出成员方法外,还存在一种特殊类型的方法,那就是构造方法。构造方法是一个与类同名的方法,对象的创建就是通过构造方法完成的。每当类实例化一个对象时,类都会自动调用构造方法。

构造方法的特点:
1.构造方法没有放回值
2.构造方法的名称要与本类的名称相同。
注意:
在定义构造方法是,构造方法没有放回值,但这与普通没有放回值的方法不同,
普通没有返回值的方法使用 public void methodEx() 这种形式进行定义,
但构造方法并不需要使用 void 关键字进行修饰。

构造方法的定义语法格式如下:

public Book() {
        // 构造方法体
}

在构造方法中可以为成员变量赋值,这样当实例化一个本类对象时,相应的成员变量也将被初始化,如果类中没有明确定义构造方法,编译器会自动创建一个不带参数的默认构造方法。

如果在类中定义的构造方法都不是无参的构造方法,那么编译器也不会为类设置一个默认的无参构造方法,
当试图调用无参构造方法实例化一个对象时,编译器会报错。所以,只有在类中没有定义任何构造方法时,
编译器才会在该类中自动创建一个不带参数的构造方法。

this 关键字不仅可以调用成员方法和变量,还可以调用类中的构造方法。

例如:当顾客购买鸡蛋灌饼时,如果要求加两个蛋,店家就给饼加两个蛋;不要求时店家会默认给饼加一个蛋。

public class EggCaek {
    int eggCount;
    
    public EggCaek(int eggCount){
        this.eggCount = eggCount;
    }
    
    public EggCaek(){
        this(1);
    }

    public static void main(String[] args) {
        EggCaek cake1 = new EggCaek();
        System.out.println("顾客不要求加蛋的数量,饼里会有" + cake1.eggCount + "个蛋。");
        EggCaek caek2 = new EggCaek(2);
        System.out.println("顾客要求加两个蛋的数量,饼里会有" + cake2.eggCount + "个蛋。");
    }
}
运行结果:
顾客不要求加蛋的数量,饼里会有1个蛋。
顾客要求加两个蛋的数量,饼里会有2个蛋。

创建对象的时候,调用的是无参构造方法,然后无参构造方法里面的 this(1) 就调用了上面的带参构造方法,将 1 赋值给了 eggCount 这个形参。

静态变量和静态方法

由 static 修饰的变量和方法称为静态变量和静态方法。

有时,在处理问题时会需要两个类在同一个内存区域共享一个数据。例如,在球类中使用圆周率 PI 这个值,可能除了本类需要这个值,在另外一个圆类中也需要使用。这时,没有必要在这两个类中同时穿件 PI ,因为这样系统会将这两个不在同一个类中定义的静态值分配到不同的内存空间中。为了解决这个问题,可以将 PI 设置为静态的。

在声明为 static 的变量和方法被称为静态成员。静态成员属于类所有,区别于个别对象,可以在本类或者其他类使用类名和 “ . ” 运算符调用静态成员。

语法:
    类名.静态类成员

创建 StaticDome 类,在类中使用 static 关键字定义一个属性和一个方法,并在主方法中调用。

public class StaticDemo {
    static double PI = 4.1415;
    
    public static void method() {
        System.out.println("这是静态方法");
    }

    public static void main(String[] args) {
        // 调用静态变量
        System.out.println(StaticDemo.PI);
        // 调用静态方法
        StaticDemo.method();
    }
}
运行结果:
    3.1415
    这是静态方法

注意:虽然静态成员也可以使用“ 对象.静态成员 ” 的形式进行调用,但是通常不建议用这样的形式,
因为这样容易混淆静态成员和给静态成员。

静态变量与静态方法的作用通常是为了提供共享数据或方法,如数学计算公式等。尽管使用这种方式调用静态成员比较方便,但静态成员同样遵循着 public private 和 protected 修饰符的约束。

在 Cust 类中创建一个静态整数类型属性 count ,在构造方法中让 count 自增。

public class Cust {
    static int count = 0;
    String name;
    
    public Cust(String name) {
        this.name = name;
        count++;
    }

    public static void main(String[] args) {
        Cust c1 = new Cust("tom");
        System.out.println("我是第" + Cust.count + "名顾客,我叫" + c1.name);
        Cust c2 = new Cust("张三");
        System.out.println("我是第" + Cust.count + "名顾客,我叫" + c2.name);
        Cust c3 = new Cust("狗蛋儿");
        System.out.println("我是第" + Cust.count + "名顾客,我叫" + c3.name);
    }
}
运行结果:
    我是第1名顾客,我叫tom
    我是第2名顾客,我叫张三
    我是第3名顾客,我叫狗蛋儿

从这个结果可以看出,因为 count 是用 static 修饰的,对于所有顾客来说这是一个共享的属性,每创建一个顾客, count 这个属性都会加 1 ,所有最后 count 统计出来的就是顾客的总人数。

如果在执行类时,希望想执行类的初始化动作,可以使用 static 定义一个静态区域,这块区域被称为静态代码块,当类文件被执行时,会首先执行 static 快中的程序,并且只会执行一次。静态代码块的语法如下:

public class example {
    static {
        // 静态代码块中的代码
    }
}
总结:
    1.在静态方法中不可以使用 this 关键字。
    2.在静态方法中不可以直接调用非静态方法。
    3.局部变量不可以使用 static 关键字声明。
    4.主方法必须用 static 声明。
    5.只有内部类可以使用 static 关键字声明。

对象

java 是一门面向对象的程序设计语言,对象是有类实例化而来的,所有问题都通过对象来处理。对象可以通过操作类的属性和方法来解决相应的问题,所有了解对象的产生、操作和消亡是十分重要的。

对象可以认为是在一类事物中抽象出某一个特例,可以通过这个特例来处理这类事物出现的问题。在 java 语言中,通过 new 操作符来创建对象。前面在在构造方法中说过,每实例化一个对象就会自动调用一次构造方法,实际上这个过程就是创建对象的过程。准确的说,可以在 java 语言中使用 new 操作符调用构造方法创建对象。语法如下:

Test test = new Test();
Test test = new Test("a");
设置值 描述 设置值 描述
Test 类名 new 创建对象操作符
test 创建 Test 类对象 "a" 构造方法的参数
注意:在 java 中,对象和实例事实上可以通用。

创建人类(people 类),类名有名字、年龄和性别3个属性,并为 people 类创建有参和无参两种构造方法。以人类为模板,创建两个对象,分别为 23 岁名叫 tom 的小伙子、19 岁名叫 lily 的小姑娘。

public class People {
    String name;
    int age;
    String sex;
    
    public People() {
    }
    
    public People(String name, int age, String sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
    }

    public static void main(String[] args) {
        People p1 = new People("tom", 23, "男");
        People p1 = new People("lily", 19, "女");
    }
}

访问对象的属性和行为

用户使用 new 操作符创建一个对象后,可以使用“对象.类成员”来获取对象的属性和行为,前文已经提到过,对象的属性和行为在类中是通过类的成员变量和成员方法的形式进行表示的,所以当对象获取类成员时,也相应地获取了对象的属性和行为。

在 Dog 类中创建名字,颜色和声音3个属性,再创建一个“叫”的方法,以 Dog 类为模板创建两只狗,一只白色且会汪汪汪叫的毛毛,一只是灰色且会嗷呜叫的灰灰。

public class Dog {
    String name;
    String color;
    String vioce;
    
    public Dog(String name, String color, Sting vioce) {
        this.name = name;
        this.color = color;
        this.vioce = vioce;
    }
    
    public void call() {
        System.out.println(vioce);
    }

    public static void main(String[] args) {
        Dog d1 = new Dog("毛毛", "白色", "汪汪汪");
        System.out.println(d1.name + "的颜色是" + d1.color);
        System.out.println(",叫起来的声音:");
        d1.call();
        
        Dog d2 = new Dog("灰灰", "灰色", "嗷呜");
        System.out.println(d2.name + "的颜色是" + d2.color);
        System.out.println(",叫起来的声音:");
        d2.call();
    }
}
运行结果:
    毛毛的颜色是白色,叫起来的声音:汪汪汪
    灰灰的颜色是灰色,叫起来的声音:嗷呜

对象的引用

在 java 语言中,尽管一切都可以看作对象,但真正的操作标识符实质上是一个引用。语法如下:

类名.对象的引用变量

例如,一个 People 类的引用可以使用一下代码:

People tom;

通常一个引用不一定需要有一个对象相关联。引用与对象相关联的语法如下:

People tom = new People();

实际上真正的对象是 “new People()” 这段代码,为了方便开发者保存、调用对象,于是创建了一个 People 类型,名叫 tom 的引用变量。实际上, tom 只是一段内存地址,用于标记 “new PEople()” 对象在内存中的位置,因为内存地址又长又乱,很难让人记住,所以 java 语言利用引用变量帮开发者标记内存地址。开发者只要记住引用变量的名字,就能够在内存里找到对象数据,简单来说,tom 是 new People() 的“代理人”。

既然 tom 是 new People() 的“代理人”,那么“new People()”对象能做的事,tom 也能做,例如下面这行代码:

new People().getClass();
等价于:
People tom = new People();
tom.getClass();

对象销毁

每个对象都有生命周期,当对象的生命周期结束时,分配给该对象的内存地址需要被回收。在其他语言中,需要用户手动回收废弃的对象。 java 拥有一套完整的垃圾回收机制,用户不必担心废弃的对象占用内存,垃圾回收器会自动回收无用却占用内存的资源。

虽然 java 的垃圾回收机制已经很完善,但垃圾回收器只能回收那些有 new 操作符创建的对象。某些对象不是通过 new 操作符在内存中获取存储空间的,这种对象无法呗累计回收机制所识别。在 java 中,提供一个 finalize() 方法,这个方法是 Object 类的方法,它被声明为 protected,用户可以在自己的类中定义这个方法。如果用户在类中定义了 finalize() 方法,在垃圾回收时会首先调用该方法,在下一次垃圾回收动作发生时,才真正回收被对象占用的内存。

说明:需要明确的是,垃圾回收或 finalize() 方法并不保证一定会发生。如果java虚拟机内存损耗殆尽,他将不会执行垃圾回收处理。

由于垃圾回收不受人为控制,具体执行时间也不确定,所以 finalize() 方法也就无法执行。为此,java 提供了 System.ge() 方法来强制启动垃圾回收器,这与给 120 打电话通知医院来救护病人的道理一样,主动告知垃圾回收器来进行清理。

推荐阅读