欢迎访问悦橙教程(wld5.com),关注java教程。悦橙教程  java问答|  每日更新
页面导航 : > > 文章正文

Java编程拾遗『对象和类』,java编程拾遗

来源: javaer 分享于  点击 41385 次 点评:148

Java编程拾遗『对象和类』,java编程拾遗


要讲Java中对象和类,Java面向对象的特性是不可避免的,Java中的对象和类其实就来自面向对象的编程思想。在之前的文章Java编程拾遗『Java概述』中,简述了Java面向对象的特性,本篇文章将重新介绍一下Java面向对象的编程思想及Java中对象和类的一些概念和使用。

1. 面向对象思想

1.1 面向对象 VS 面向过程

面向对象是一种编程思想,是当今软件开发方法中的主流方法之一,它把数据及对数据的操作方法整合在一起,作为一个相互依存的整体,即对象。对同类食物抽象出其共性,即类,类中的大多数数据,只能被本类的方法进行处理,类通过外部接口和外界发生关系。比如,站在抽象的角度,人具有身高、年龄、体重、血型等特征,同时具有会劳动、会直立行走、会吃饭等这些方法,人仅仅是一个抽象的概念,并不是一个存在的实体,所有具备人这个群体的属性与方法的对象都可以叫人。这个对象是实际存在的实体,每个人实体都是人这个群体的一个对象。

面向过程是一种一时间为中心的开发方法,就是自上到下的顺序执行,逐步求精,其程序结构是按照功能划分成若干个基本模块,这些基本模块形成一个属性结构,各模块之间的关系也非常简单,在功能上相对独立,每一个模块内部一般都是由顺序、选择和循环三种基本结构组成。面向过程的模块化是由子程序实现的,程序流程在写程序时就已经决定了。以五子棋为例,面向过程的设计思路就是首先分析问题的步骤:第一步,开始游戏。第二步,黑子先走。第三步,绘制画面。第四步,判断输赢。第五步,白子走棋。第六步,绘制画面。第七步,判断输赢。第八步,返回第二步。第九步,输出最后结果。如果把上面每个步骤都分别用一个函数来实现,就是一个面向过程的开发方法。

1.2 面向对象和面向过程的不同点

1.3 面向对象的特征

面向对象主要包括抽象、继承、封装、多态四个特征,这里简单讲一下。

1.4 面向对象的优点

2. 类

上面讲述了面向对象开发思想的一些概念,Java中面向对象实现的载体就是类,类其实就是对于现实事物抽象的封装,并且Java语言赋予了类继承、多态等面向对象的特性。Java中类的基本格式:

[类定义修饰符] class <类名> {
	[类变量声明]
	[成员变量声明]
	[构造函数]
	[类方法]
	[成员方法]
	[成员变量的get/set方法]
}
  • 类定义修饰符:类定义修饰符定义了类的访问权限,可以省略,也可以是public, protected, private, static, final,其中public、protected , private三个最多只能出现其中之一,static和final可一起出现。
    • public:公有权限,该类在任何地方都可访问。
    • protected:受保护权限,只允许修饰内部类,表示在子类和本包中可见
    • private:私有权限,只允许修饰内部类,表示只在本类中可见
    • 缺省:包访问权限,同一package可用
    • static:静态类,如果一个类要被声明为static的,只有一种情况,就是静态内部类。如果在外部类声明为static,程序会编译都不会过。
    • final:修饰class时,表示类为不可变类,类的对象一旦被创建就不能被修改了,比如String类。
  • 类名:类名使用UpperCamelCase风格。
  • 类变量:static修饰的变量,变量为类持有,所有对象共享该变量。
  • 实例变量:非static修饰的变量,变量为对象持有,每个对象持有一个该成员变量。
  • 构造函数:构造对象实例的方法。
  • 类方法:static修饰的方法,也叫静态方法。属于整个类的,不是属于某个实例的,只能处理static域或调用static方法。
  • 实例方法:非static修饰的方法,属于对象的方法,由对象来调用。

2.1 类定义修饰符

上面讲过public, protected, private, static, final都可以用来修饰class,其中public、protected , private三个最多只能出现其中之一,static和final可一起出现,下面分别讲一下这几种修饰符。

2.1.1 public

public权限表示共有权限,时日常开发中最常见也是最简单的一种权限。因为时公有权限,所以在任何地方使用都不受限制,这里不多解释了。

2.1.2 protected

受保护权限,在修饰class时只允许修饰内部类,表示在子类和本包中可见。这里要注意的时,不是本包下,protected内部类不可见,即使是子类也不行。

package com.zhuoli.service.thinking.java.clazz;

@Getter
@Setter
public class ProtectedClass {
    private String name;

    private Integer age;

    protected void print() {
        System.out.println("protected method");
    }

    @Getter
    @Setter
    protected class InnerProtectedClass {
        private String innerName;
    }

    @Test
    public void test() {
        //protected内部类在本类中可见
        InnerProtectedClass innerProtectedClass = new InnerProtectedClass();
        innerProtectedClass.setInnerName("k8s");
        System.out.println(innerProtectedClass.getInnerName());
    }
}
package com.zhuoli.service.thinking.java.clazz.other;

public class ProtectedClassTest3Sub extends ProtectedClass {

    @Test
    public void test(){
        //父类protected方法子类可见,无论子类是不是与父类在同一个包下
        print();

        //子类与父类不在同一个包下,子类实例可以访问其从基类继承而来的protected方法
        ProtectedClassTest3Sub protectedClassTest3Sub = new ProtectedClassTest3Sub();
        protectedClassTest3Sub.print();

        //子类与父类不在同一个包下,则父类实例不能访问父类的protected方法,以下编译报错
        ProtectedClass protectedClass = new ProtectedClass();
        protectedClass.print();

        //注意protected内部类的继承规则跟protected成员方法略有不同,protected内部类严格遵守同包和子类可见条件,非同包即使时子类也不可见,以下报错
        InnerProtectedClass innerProtectedClass1 = new InnerProtectedClass();

        //这种方式也不行,因为不同包
        ProtectedClassTest3Sub protectedClassTest3Sub1 = new ProtectedClassTest3Sub();
        ProtectedClassTest3Sub.InnerProtectedClass innerProtectedClass2 = protectedClassTest3Sub1.new InnerProtectedClass();
    }
}

以上可以看出,父类protected内部类,访问权限是本包、子类,但是子类必须与父类在同一个包下,否则也是不可见的。而对于父类protected成员,访问权限也是本包及子类,但是不要求子类必须与父类在同一包下,若子类与基类不在同一包中,那么在子类中,子类实例可以访问其从基类继承而来的protected方法,基类实例不能访问父类的protected方法。

2.1.3 private

跟protected一样,在修饰class时,只允许修饰内部类,表示只在本类中可见。对于可见性没什么好讲的,只允许在本类中出现。这里借助private内部类讲一下使用内部类使用的一个好处——实现多重继承。在Java中是不允许多重继承的,但是可以使用内部类变相实现多重继承。

public class Father {
    public int strong(){
        return 9;
    }
}

public class Mother {
    public int kind(){
        return 8;
    }
}

如上存在两个类Father和Mother类,对于一个子类Son,如果想同时继承这两个类,显然是不可以的,但是可以通过内部类实现一种折中的多重继承,如下:

public class Son {

    private class Father_1 extends Father{
        public int strong(){
            return super.strong() + 1;
        }
    }

    private class Mother_1 extends  Mother{
        public int kind(){
            return super.kind() - 2;
        }
    }

    public int getStrong(){
        return new Father_1().strong();
    }

    public int getKind(){
        return new Mother_1().kind();
    }

    public static void main(String[] args) {
        Son son = new Son();
        System.out.println("Son's Strong:" + son.getStrong());
        System.out.println("Son's kind:" + son.getKind());
    }
}

这一点在《Thinking in Java》中有讲到,有兴趣的同学可以取了解一下。

2.1.4 缺省

缺省表明权限是包访问权限,只在本包内可访问。

package com.zhuoli.service.thinking.java.clazz.default0;

@Getter
@Setter
class DefaultClass {
    private String name;

    private Integer age;

    void print() {
        System.out.println("protected method");
    }

    @Getter
    @Setter
    class InnerDefaultClass {
        private String innerName;
    }
}
package com.zhuoli.service.thinking.java.clazz.default0;

public class DefaultClassTest {
    DefaultClass defaultClass = new DefaultClass();
    //同包中可见
    private DefaultClass.InnerDefaultClass innerDefaultClass = defaultClass.new InnerDefaultClass();

    @Test
    public void test(){
        innerDefaultClass.setInnerName("inner class");
        System.out.println(innerDefaultClass.getInnerName());

        //包访问权限,父类实例可以在同包下调用自己的protected方法
        defaultClass.print();
    }
}
package com.zhuoli.service.thinking.java.clazz.default0.other;

public class OtherPackageTest {

    @Test
    public void test() {
        /*DefaultClass是包访问权限,在不同包下不可见,以下编译报错*/
        DefaultClass defaultClass = new DefaultClass();
    }
}

本包外,DefaultClass都是不可见的。其实Java看以上三种访问权限可以发现,public和private最干净,一个是不受限,另一个只在本类中可见。protected在修饰类时,只能修饰内部类,表明类权限是子类可见及同包可见,通过上述示例代码可以发现,protected内部类的可见性其实就是在本包内(子类可见的前提也是子类父类在同一个包下)。类定义修饰符缺省的情况下,类只能在本包中可见。其实通常讲public、protected、default、private修饰权限其实都是在讲修饰成员变量时,成员变量的可见性,权限如下表:

  当前类 同一package 子类 其它package
public
protected ×
default × ×
private × × ×

对于protected权限,这里单独讲一下其他package的说明,如果子类与基类不在同一包中,那么在子类中,子类实例可以访问其从基类继承而来的protected方法,父类实例不能访问其protected方法,也就是说其它package不可见不是绝对的。对于protected访问权限,可以参考这篇文章,讲的很清楚。

2.1.5 static

在Java世界里,经常被提到静态这个概念,static作为静态成员变量和成员函数的修饰符,意味着它为该类的所有实例所共享,也就是说当某个类的实例修改了该静态成员变量,其修改值为该类的其它所有实例所见。如果一个类要被声明为static的,只有一种情况,就是静态内部类。如果在外部类声明为static,如果外部类被定义为static,则会编译报错。Oracle官方给出了内部类和静态内部类的区别:

Nested classes are divided into two categories: static and non-static. Nested classes that are declared static are called static nested classes. Non-static nested classes are called inner classes.

从字面上看,一个被称为静态嵌套类,一个被称为内部类。嵌套的意思,是可以完全独立存在意思,只是想借用外部类的壳用一下,隐藏一下自己。它是一个独立的类,完全是形式上的“内部”,和外部类没有本质上“内外”的关系,如下:

静态内部类使用场景一般是当外部类需要使用内部类,而内部类无需外部类资源,并且内部类可以单独创建的时候会考虑采用静态内部类的设计,一个常见的用法就是通过静态内部类实现builder模式:

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

    public static class Builder {
        private String name;
        private int age;

        public Builder() {
            return this;
        }

        public Builder withName(String name) {
            this.name = name;
            return this;
        }

        public Builder withAge(int age) {
            this.age = age;
            return this;
        }

        public Outer build() {
            return new Outer(this);
        }
    }

    private Outer(Builder b) {
        this.age = b.age;
        this.name = b.name;
    }
}

静态内部类调用外部类的构造函数,来构造外部类,由于静态内部类可以被单独初始化,所以可以如下调用:

Outer outer = new Outer.Builder().withName("zhuoli").withAge(18).build();
return outer;

2.1.6 final

当一个类被final修饰时,表明此类是个不可变类,不能被继承,所有的方法都不能被重写。但这并不表示final类的成员变量也是不可改变的,要想做到final类的成员变量不可改变,必须给成员变量增加final修饰。

2.2 类名

《码出高效 阿里巴巴Java开发手册》中讲到,类名使用 UpperCamelCase 风格,但以下情形例外:DO/BO/DTO/VO/AO/PO/UID 等。

正例:MarcoPolo / UserDO / XmlService / TcpUdpDeal / TaPromotion
反例:macroPolo / UserDo / XMLService / TCPUDPDeal / TAPromotion

2.3 类变量

类变量也叫静态变量,是指类中被static修饰的成员变量,实质上就是一个全局变量。如果某个内容是被所有对象所共享,那么该内容就应该用static修饰,没有被static修饰的内容,其实是属于对象的特殊描述。类变量在类加载时,在堆中分配内存,类变量的生命周期一直持续到类卸载,并且被所有对象所共享。

2.4 实例变量

实例变量也叫非static成员变量,是指类中没有被static修饰的成员变量,用于描述对象特征。实例变量是属于特定的对象的,每个对象持有的实例变量都有可能是不同的,在创建对象时在堆上分配内存,当对象被回收时,实例变量的生命周期也相应结束。

2.5 构造函数

构造函数是一种特殊的函数,用来在对象实例化时初始化对象的成员变量。在Java中,构造函数具有以下特点:

2.6 类方法

类方法也叫静态方法,是指通过static修饰的成员方法,用于描述类行为,在调用方式上可以直接使用类名调用,而不用通过实例调用。在同一个类中,实例方法内部可以直接使用静态方法,但是静态方法中则无法直接调用这个实例方法,相应的静态方法也只能访问静态成员。关于静态方法,相信很多人都有一种熟悉又陌生的感觉,下面简单讲一下关于静态方法和非静态方法的一些误解:

那么为什么还要区分静态方法和实例化方法 ?

如果我们继续深入研究的话,就要脱离技术谈理论了。早期的结构化编程,几乎所有的方法都是“静态方法”,引入实例化方法概念是面向对象概念出现以后的事情了,区分静态方法和实例化方法不能单单从性能上去理解,创建c++,java,c#这样面向对象语言的大师引入实例化方法一定不是要解决什么性能、内存的问题,而是为了让开发更加模式化、面向对象化。这样说的话,静态方法和实例化方式的区分是为了解决模式的问题。

2.7 实例方法

实例方法就是类中那些没有被static修饰的方法,属于对象行为,在讲静态方法时,已经讲了静态方法和实例方法的区别,这里不多阐述了。

3. 基础概念答疑

当涉及到类和概念时,总有一些基础概念,当遇到时感觉很简单,但要表述时往往又不能完全表述清楚,这里总结以下一些基本概念。

3.1 继承

继承是面向对象的一个重要特性,通过继承,子类可以使用父类中的一些成员变量和方法,从而提高代码的复用性,提高开发效率。Java中继承主要有以下几个特性:

3.2 类之间的关系

Java中,类之间最常见的关系有以下三种:

  • 依赖(“uses-a”)
  • 聚合(“has-a”)
  • 继承(“is-a”)

依赖(dependence),即”uses-a”关系,是一种最明显、最常见的关系。如下:

public class Order{

	public OrderVO getOrder(GetOrderRequest request) {
		Account account = AccountService.getAccount(**);
		……
	}
}

Order类中使用了Account类,这时候Order类与Account就是依赖关系(“uses-a”)。开发中应该该尽可能地将相互依赖地类减至最少。如果类A不知道B地存在,它就不会关心类B地任何改变(类B地改变不会导致A产生任何bug)。用途软件工程地属于来说,就是让类之间地耦合度最小。

聚合(aggregation),即”has-a”关系,是一种具体且易于理解的关系。如下:

public class Order{

	private Account account;
	public Order(Account account){
		this.account = account;
	}
	public OrderVO getOrder(GetOrderRequest request) {
		 Long accountNo = account.getAccountNo();
		……
	}
}

Order类中持有了一个Account对象,这时候Order类和Account类就是”has-a”的关系。

继承(inheritance),即”is-a”关系,是一种表示特殊与一般的关系,其实即时父类与子类的关系。如下:

public class Order{
	private Long orderNo;

	private Long price;

	//getter and setter
}

public class Xorder extends Order{
	……
}

Xorder类继承了Order类,Xorder类与Order类就是”is-a”关系。

继承和组合是开发中最常见的两种代码常用方式,在开发中,一般遵循以下规则:

3.3 多态

多态是面向对象程序设计中代码重用的一个重要机制,他表示当一个操作作用在不同的对象时,会有不同的语义,从而产生不同的结果。在Java中,多态主要有以下两种实现方式:

重载:

覆盖:

参考链接:

相关文章

    暂无相关文章
相关栏目:

用户点评