第四章 类与对象

第四章 类与对象

4.1 面向对象

封装

封装是面向对象的方法所应遵循的一个重要原则。封装具有两个含义:一是指把对象的成员属性和行为看成一个密不可分的整体,将这两者“封装”在一个不可分割的独立单位(对象)中。另一层含义是指“信息隐蔽”,把不需要让外界知道的信息隐藏起来。有些对象的属性及行为允许外界用户知道或使用,但不允许更改;而另一些属性或行为,则不允许外界知晓,或者只允许使用对象的功能,而尽可能隐蔽对象的功能实现细节。
封装机制在程序设计中表现为,把描述对象属性的变量与实现对象功能的方法组合在一起,定义为一个程序结构,并保证外界不能任意更改其内部的属性值,也不能任意调动其内部的功能方法。
封装机制的另一个特点是,给封装在一个整体内的变量及方法规定了不同级别的“可见性”或访问权限。

继承

继承是面向对象的方法中的重要概念,是提高软件开发效率的重要手段。
首先继承拥有反映事物一般特性的类;其次在其基础上派生出反映特殊事物的类。
在Java程序设计中,对于继承实现前一定要有一些已经存在的类(可以是自定义的类或者由类库所提供的类)。用户开发的程序类需要继承这些已有的类。这样,新定义的类结构可以继承已有类的结构(属性或方法)。被继承的类称为父类或超类,而经继承产生的类称为子类或派生类。根据继承机制,派生类继承了超类的所有内容,并相应地增加了自己的一些新的成员。
面向对象程序设计中的继承机制,大大增强了程序代码的可重复使用性,提高了软件的开发效率,降低了程序产生错误的可能性,也为程序的修改扩充提供了便利。
若一个子类只允许继承一个父类,称为单继承;若允许继承多个父类,则称为多继承。目前Java程序设计语言不支持多继承。而Java语言通过接口(interface)的方式来弥补由于Java不支持多继承而带来的子类不能享用多个父类的成员的缺憾。

多态

多态是面向对象程序设计的又一个重要特征。多态是指允许程序中出现重名现象,Java语言中含有方法重载与对象多态两种形式的多态。
方法重载:在一个类中,允许多个方法使用同一个名字,但方法的参数不同,完成的功能也不同。
对象多态:子类对象可以与父类对象进行相互的转换,而且根据其使用的子类的不同完成的功能也不同。

4.2 类与对象

一个类的基本组成单元有两个。

  • 成员属性(field): 主要用于保存对象的具体特征。
  • 方法(method): 用于描述功能。

4.2.1 类与对象的定义

类的定义基础语法如下:

class 类名称 {
	[访问修饰符] 数据类型 成员属性(变量);
	....
	public 返回值的数据类型 方法名称(参数类型 参数1, 参数类型 参数2, ....){
		程序语句;
		[return 表达式;]
	}
}

范例:类的定义

class Person {
    String name;
    int age;
    public void tell() {
        System.out.println("姓名:" + name + "、年龄:" + age);
    }
}

对象定义

声明并实例化对象类对象 对象名称 = new 类名称();
声明对象类名称 对象名称 = null;
实例化对象对象名称 = new 类名称();

在Java中引用数据类型是需要进行内存分配的,所以在定义时必须通过关键字new来分配相应的内存空间后才可以使用,此时该对象也被称为“实例化对象”,而一个实例化对象就可以采用以下的方式进行类结构的操作。

  • 对象.成员属性: 表示调用类之中的成员属性,可以为其赋值或者获取其保存内容。
  • 对象.方法(): 表示调用类之中的方法。

范例:通过实例化对象进行类操作

class Person {
    String name;
    int age;
    public void tell() {
        System.out.println("姓名:" + name + "、年龄:" + age);
    }
}
public class JavaDemo {
    public static void main(String[] args) {
        Person person = new Person();
        person.name = "张三";
        person.age = 18;
        person.tell();
    }
}

4.2.2 对象内存分析

Java中类属于引用数据类型,所有的引用数据类型在使用过程中都要通过关键字new开辟新的内存空间,当对象拥有了内存空间后才可以实现成员属性的信息保存,在引用数据类型操作中最为重要的内存有两块。

  • 【heap】堆内存: 保存的对象的具体信息(成员属性),在程序之中堆内存空间的开辟时通过 new 完成的。
  • 【stack】栈内存: 保存的是一块堆内存的地址,即通过地址找到堆内存,而后找到对象内容。

4.2.3 对象引用传递分析

类是一种引用数据类型,而引用数据类型的核心本质在于堆内存和栈内存的分配与指向处理。在程序开发中,不同的栈内存可以指向同一块的堆内存空间(相当于为同一块堆内存设置不同的对象名称),这样就形成了对象的引用传递过程。

范例:引用传递

class Person {
    String name;
    int age;
    public void tell() {
        System.out.println("姓名:" + name + "、年龄:" + age);
    }
}
public class JavaDemo {
    public static void main(String[] args) {
        Person person = new Person();
        person.name = "张三";
        person.age = 18;
        Person person1 = person;
        person1.age = 22;
        person.tell();
    }
}

在示例代码中 "Person person1 = person;" ,该语句实现将person对象的地址赋值给person1,这样两个对象都指向了同一块内存空间。

在实际项目开发中,引用传递使用最多的情况是结合方法来使用,即可以通过方法的参数接收引用对象,也可以通过方法返回一个引用对象。

范例:通过方法实现引用传递

class Person {
    String name;
    int age;
    public void tell() {
        System.out.println("姓名:" + name + "、年龄:" + age);
    }
}
public class JavaDemo {
    public static void main(String[] args) {
        Person person = new Person();
        person.name = "张三";
        person.age = 18;
        change(person);
        person.tell();
    }
    public static void change(Person p) {
        p.age = 50;
    }
}

本程序定义了change()方法,并且在方法上接收了Person类型的引用对象,这样当通过change()方法的temp对象进行属性修改的时候将会影响到原始对象内容。

4.2.4 引用传递与垃圾产生分析

引用传递的本质意义在于,一块堆内存空间可以被不同的栈内存所引用,每一块栈内存都会保存有堆内存的地址信息,并且只允许保存一个堆内存地址信息,即如果一块栈内存已经存在有其他堆内存的引用,当需要改变引用指向时就需要丢弃已有的引用实体更换为新的引用实体。

范例:引用传递与垃圾产生

class Person {
    String name;
    int age;
    public void tell() {
        System.out.println("姓名:" + name + "、年龄:" + age);
    }
}
public class JavaDemo {
    public static void main(String[] args) {
        Person p1 = new Person();
        Person p2 = new Person();
        p1.name = "张三";
        p1.age = 20;
        p2.name = "李四";
        p2.age = 25;
        p2 = p1;
        p2.age = 50;
        p1.tell();
    }
}

本程序实例化了两个Person类对象(p1和p2),并且分别为这两个对象进行赋值,但是由于发生了引用传递 "p2=p1" ,所以p2将丢弃原始的引用实体(产生垃圾),将引用指向p1的实体,这样当执行 "p2.age=80" 语句时修改的就是p1对象的内容。

p1和p2两个栈内存都各自保存着一块堆内存空间指向,而每一块栈内存只能够保留一块堆内存空间的地址。所以当程序发生了引用传递(p2=p1)时,p2首先要断开已有的堆内存连接,而后才能够指向新的堆内存(p1所指向的堆内存)。但是由于此时p2原本指向的堆内存空间没有任何栈内存对其进行引用,该内存空间就将成为垃圾空间,所有的垃圾空间将等待GC(Garbage Collection,垃圾收集器)不定期地进行回收释放。

4.3 成员属性封装

面向对象的第一大特性指的就是封装性,而封装性最重要的特点就是内部结构对外不可见。之前的例子中属性是可以通过对象直接调用的,但是这样是不安全的,所以我们需要用 private 关键字进行封装。如果外部需要访问属性,可以使用 setter()getter() ,方法进行处理。

  • setter(private String name): public void setName(String name);
  • getter(private String name): public String getName();

范例:使用private实现封装

class Person {
    private String name;
    private int age;
    public void tell() {
        System.out.println("姓名:" + name + "、年龄:" + 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 class JavaDemo {
    public static void main(String[] args) {
        Person p1 = new Person();
        p1.setName("张三");
        p1.setAge(18);
        p1.tell();
    }
}

4.4 构造方法与匿名对象

构造方法是在类中定义的一种特殊方法,它在一个类使用关键字new实例化新对象时默认调用,其主要功能是完成对象属性的初始化操作。

构造方法补充说明

类名称 对象名称 = new 类名称();
类名称:对象所有的功能必须由类定义,也就是说本操作是告诉程序类所具有的功能。
对象名称:实例化对象的唯一标注,在程序之中利用此标注可以找到一个对象。
new:类属于引用数据类型,所以对象的实例化一定要用new开辟堆内存空间。
类名称():一般只有在方法定义的时候才需要加上“()”,这个就表示调用构造方法。

构造方法定义要求

  • 构造方法的名称和类名称保持一致。
  • 构造方法不允许有返回值类型声明。

注意: 由于对象实例化操作一定需要构造方法的存在,所以如果在类之中没有明确定义构造方法的话,则会自动生成一个无参数并且无返回值的构造方法,供用户使用;如果一个类中已经明确定义了一个构造方法,则不会自动生成无参且无返回值的构造方法,也就是说,一个类中至少存在一个构造方法。

范例:默认情况下会存在一个无参构造方法

class Person {
    public Person() {}
}

范例:定义构造方法为属性初始化

class Person {
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    private String name;
    private int age;
    public void tell() {
        System.out.println("姓名:" + name + "、年龄:" + 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 class JavaDemo {
    public static void main(String[] args) {
        Person p1 = new Person("张三", 22);
        p1.tell();
    }
}

范例:构造方法重载

class Person {
    public Person() {}
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    private String name;
    private int age;
    public void tell() {
        System.out.println("姓名:" + name + "、年龄:" + 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 class JavaDemo {
    public static void main(String[] args) {
        Person p1 = new Person("张三", 22);
        p1.tell();
    }
}

范例:使用构造方法接收引用数据类型

class Message {
    private String info;
    public Message(String info) {
        this.info = info;
    }
    public String getInfo() {
        return info;
    }
    public void setInfo(String info) {
        this.info = info;
    }
}
class Person {
    private String name;
    private int age;
    public Person(Message msg, int age) {
        name = msg.getInfo();
        this.age = age;
    }
    public Message getMessage() {
        return new Message("姓名:" + name + "、年龄:" + age);
    }
    public void tell() {
        System.out.println("姓名:" + name + "、年龄:" + 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 class JavaDemo {
    public static void main(String[] args) {
        Person p1 = new Person(new Message("张三"), 25);
        p1.tell();
        Message message = p1.getMessage();
        System.out.println("message = " + message);
    }
}

4.5 this关键字

this 描述的是本类结构调用的关键字,在Java中this关键字可以描述3种结构的调用。

  • 当钱类中的属性:this.属性
  • 当前类中的方法(普通方法、构造方法):this()this.方法名称()
  • 描述当前对象。

4.5.1 this调用本类属性

范例:通过"this.成员属性"访问

class Person {
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    private String name;
    private int age;
    public void tell() {
        System.out.println("姓名:" + name + "、年龄:" + 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 class JavaDemo {
    public static void main(String[] args) {
        Person p1 = new Person("张三", 22);
        p1.tell();
    }
}

4.5.2 this调用本类方法

  • 调用本类普通方法:使用 this.方法() 调用,并且可以在构造方法与普通方法中使用。
  • 调用本类构造方法:调用本类其他构造方法使用 this() 形式,此语句只允许放在构造方法首行使用。

范例:使用this调用本类普通方法

class Person {
    public Person(String name, int age) {
        this.setName(name);
        this.age = age;
    }
    private String name;
    private int age;
    public void tell() {
        System.out.println("姓名:" + name + "、年龄:" + 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 class JavaDemo {
    public static void main(String[] args) {
        Person p1 = new Person("张三", 22);
        p1.tell();
    }
}

范例:使用this()实现本类构造方法的相互调用

class Person {
    private String name;
    private int age;
    public Person(String name) {
        this();
        this.age = age;
    }
    public Person(String name, int age) {
        this(name);
        this.age = age;
    }
    public void tell() {
        System.out.println("姓名:" + name + "、年龄:" + 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 class JavaDemo {
    public static void main(String[] args) {
        Person p1 = new Person("张三", 22);
        p1.tell();
    }
}

4.5.3 this表示当前对象

一个类可以实例化出若干个对象,这些对象都可以调用类中提供的方法,那么对于当前正在访问类中方法的对象就可以称为当前对象,而this就可以描述出这种当前对象的概念。

范例:实现消息发送逻辑

class Message {
    private Channel channel;
    private String title;
    private String content;
    public Message (Channel channel, String title, String content) {
        this.channel = channel;
        this.title = title;
        this.content = content;
    }
    public void send() {
        if (this.channel.isConnect()) {
            System.out.println("title = " + this.title + "、content= " + this.content);
        }else {
            System.out.println("无法建立连接");
        }
    }
}
class Channel {
    private Message message;
    public Channel(String title, String content) {
        this.message = new Message(this, title, content);
        this.message.send();
    }
    public boolean isConnect() {
        return true;
    }
}
public class JavaDemo {
    public static void main(String[] args) {
        Channel channel = new Channel("www", "baidu.com");
    }
}

4.6 static关键字

static是一个用于声明程序结构的关键字,此关键字可以用于全局属性和全局方法的声明,主要特点是可以避免对象实例化的限制,在没有实例化对象的时候直接进行此类结构的访问。

4.6.1 static属性

在一个类中,主要的组成就是属性和方法(构造方法和普通方法),而每一个对象都分别拥有各自的属性内容(不同对象的属性保存在不同的堆内存中)。如果想要类中的某个属性定义为公共属性(所有对象都可以使用的属性),则可以在声明属性前加上static关键字。

范例:定义static属性

public class JavaDemo {
    static String str = "www.baidu.com";
    public static void main(String[] args) {
        System.out.println(JavaDemo.str);
    }
}

注意:

  • 使用static定义的属性内容不在堆内存中保存,而是保存在全局数据区。
  • 使用static定义的属性内容表示类属性,类属性可以由类名称直接进行调用。
  • static属性虽然定义在了类中,但是其可以在没有实例化对象的时候进行调用(普通属性保存在堆内存里,而static属性保存在全局数据区之中)。

4.6.2 static定义方法

static除了可以进行属性定义之外,也可以进行方法的定义,一旦使用static定义了方法,那么该方法就可以在没有实例化对象的情况下直接调用。

范例:定义static方法

public class JavaDemo {
    public static void main(String[] args) {
        printInfo("www.baidu.com");
    }
    public static void printInfo(String msg) {
        System.out.println("msg = " + msg);
    }
}

注意:

  • static定义的方法不能调用非static的方法或属性。
  • 非static定义的方法可以调用static的属性或方法。
  • 使用static定义的属性和方法,可以在没有实例化对象的时候使用(如果没有实例化对象,也就没有了表示当前对象的this,所以static方法内部无法使用this关键字)。
  • 非static定义的属性和方法,必须实例化对象之后才可以进行调用。

4.7 代码块

代码块是在程序之中使用 "{}" 定义起来的一段程序,而根据代码块声明位置以及声明关键字的不同,代码块一共分为4种:普通代码块、构造代码块、静态代码块和同步代码块。

4.7.1 普通代码块

普通代码块是定义在方法中的代码块,利用这类代码块可以解决代码块在一个方法中过长导致出现重复变量定义的问题。

范例:定义普通代码块

public class JavaDemo {
    public static void main(String[] args) {
        {
            int x = 10;
            System.out.println("x = " + x);
        }
        int x = 100;
        System.out.println("x = " + x);
    }
}

4.7.2 构造代码块

将代码块定义在一个类中,这样就成为构造代码块。构造代码块的主要特点是在使用关键字new实例化新对象时进行调用。

范例:定义构造代码块

class Message {
    public Message() {
        System.out.println("【构造方法】Message类构造方法执行");
    }
    {
        System.out.println("【构造代码块】Message构造代码块执行");
    }
}
public class JavaDemo {
    public static void main(String[] args) {
        new Message();
        new Message();
    }
}

4.7.3 静态代码块

静态代码块也是定义在类中的,如果一个构造代码块上使用了static关键字进行定义的话,就表示为静态代码块。

使用静态代码块时有两种情形:

  • 在非主类中定义静态代码块。
  • 在主类中定义静态代码块。

范例:在非主类中定义的静态代码块

class Message {
    public Message() {
        System.out.println("【构造方法】Message类构造方法执行");
    }
   static {
        System.out.println("【静态代码块】Message静态代码块执行");
    }
    {
        System.out.println("【构造代码块】Message构造代码块执行");
    }
}
public class JavaDemo {
    public static void main(String[] args) {
        new Message();
        new Message();
    }
}

不管实例化多少个对象,静态代码块中的代码只会执行一次。

提示:可以利用静态代码块实现一些公共的初始化操作。

范例:在主类中定义静态代码块

class Message {
    public Message() {
        System.out.println("【构造方法】Message类构造方法执行");
    }
   static {
        System.out.println("【静态代码块】Message静态代码块执行");
    }
    {
        System.out.println("【构造代码块】Message构造代码块执行");
    }
}
public class JavaDemo {
    static {
        System.out.println("【主方法静态代码块】");
    }
    public static void main(String[] args) {
        new Message();
        new Message();
    }
}
// 执行结果:
// 【主方法静态代码块】
// 【静态代码块】Message静态代码块执行
// 【构造代码块】Message构造代码块执行
// 【构造方法】Message类构造方法执行
// 【构造代码块】Message构造代码块执行
// 【构造方法】Message类构造方法执行
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值