码出高效:Java开发手册-第2章(4)-阿里云开发者社区

开发者社区> 博文视点Broadview> 正文

码出高效:Java开发手册-第2章(4)

简介: 本章开始讲解面向对象思想,并以Java 为载体讲述面向对象思想在具体编程语言中的运用与实践。当前主流的编程语言有50 种左右,主要分为两大阵营:面向对象编程与面向过程编程。面向对象编程(Object-Oriented Programming,OOP)是划时代的编程思想变革,推动了高级语言的快速发展和工业化进程。OOP 的抽象、封装、继承、多态的理念使软件大规模化成为可能,有效地降低了软件开发成本、维护成本和复用成本。面向对象编程思想完全不同于传统的面向过程编程思想,使大型软件的开发就像搭积木一样隔离可控、高效简单,是当今编程领域的一股势不可......
+关注继续查看

2.3.3 内部类

在一个.java 源文件中,只能定义一个类名与文件名完全一致的公开类,使用public class 关键字来修饰。但在面向对象语言中,任何一个类都可以在内部定义另外一个类,前者为外部类,后者为内部类。内部类本身就是类的一个属性,与其他属性定义方式一致。比如,属性字段private static String str,由访问控制符、是否静态、类型、变量名组成,而内部类 private static class Inner{},也是按这样的顺序来定义的,类型可以为class、enum,甚至是interface,当然在内部类中定义接口是不推荐的。内部类可以是静态和非静态的,它可以出现在属性定义、方法体和表达式中,甚至可以匿名出现,具体分为如下四种。

    • 静态内部类,如:static class StaticInnerClass{};
    • 成员内部类,如:private class InstanceInnerClass{};
    • 局部内部类,定义在方法或者表达式内部;
    • 匿名内部类,如:(new Thread(){}).start()。

如下是最精简的4 种内部类定义方式:

public class OuterClass {

// 成员内部类

private class InstanceInnerClass {}

// 静态内部类

static class StaticInnerClass {}

public static void main(String[] args) {

// 两个匿名内部类,分别对应图2-1 所示的OuterClass$1 和OuterClass$2

(new Thread() {}).start();

(new Thread() {}).start();

// 两个方法内部类,分别对应图2-1 所示的OuterClass$1MethodClass1 和

// OuterClass$1MethodClass2

class MethodClass1 {}

class MethodClass2 {}

}

}

无论是什么类型的内部类,都会编译成一个独立的.class 文件,如图2-1 所示。

1.jpg

图2-1 外部类和内部类.class 文件

外部类与内部类之间使用$ 符号分隔,其中匿名内部类使用数字进行编号,而方法内部类,使用编号加方法名称来标识是哪个方法。匿名内部类和静态内部类是比较常用的方式。在本书的示例代码中经常使用匿名类来启动线程,节约了若干行代码。而静态内部类是最常用的内部表现形式,外部可以使用OuterClass.StaticInnerClass 直接访问,内部类加载与外部类通常不在同一个阶段进行,在JDK 源码中,定义包内可见静态内部类的方式很常见,这样做的好处是:

(1)作用域不会扩散到包外。

(2)可以通过“外部类. 内部类”的方式直接访问。

(3)内部类可以访问外部类中的所有静态属性和方法。

static class Node implements Map.Entry {

final int hash;

final K key;

volatile V val;

volatile Node next;

}

如上所示的源码是在ConcurrentHashMap 中定义的Node 静态内部类,用于表示一个节点数据,属于包内可见,包内其他集合要用到这个Node 时,直接使用ConcurrentHashMap.Node。仅包内可见,可以阻止外部程序随意使用此类来生成对象,Node 的父类Entry 是Map 的静态内部类,之所以可以被Node 成功继承,是因为两个外部类同属一个包。在JDK 源码中,使用内部类封装某种属性和操作的方式比较常见,比如应用类加载器Launcher 的AppClassLoader,ReentrantLock 中继承自AbstractQueuedSynchronizer 的内部类Sync,ArrayList 中的私有静态内部类SubList。内部类中还可以定义内部类,形成多层嵌套,如在ThreadLocal 静态内部类ThreadLocalMap 中还定义一个内部类Entry,如图2-2 所示。

2.jpg

图2-2 ThreadLocalMap 的内部类Entry

因为访问权限可见,所以在同一包内的Thread 可以直接使用如下方式声明自己的属性:

ThreadLocal.ThreadLocalMap threadLocals = null;

ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

2.3.4 访问权限控制

面向对象的核心思想之一就是封装,只把有限的方法和成员公开给别人,这也是迪米特法则的内在要求,使外部调用方对方法体内的实现细节知道得尽可能少。如何实现封装呢?需要使用某些关键字来限制类外部对类内属性和方法的随意访问,这些关键字就是访问权限控制符。在详细介绍访问权限之前,我们明确一个概念——是否可见,如下示例代码:

package a;

public class VisibleScope {

public void publicMethod(){}

protected void protectedMethod(){}

void noneMethod(){}

private void privateMethod(){}

}

在package b 中,图2-3 左侧对VisibleScope 类进行实例化,通过引用变量,仅能看到publicMethod() 方法,我们称为publicMethod() 方法是当前可见的;使用同一个类,图2-3 右侧对VisibleScope 子类VisibleScopeInvoke 进行实例化,通过引用变量,可以看到两个方法,新增了红色框内的protectedMethod() 方法,那么这两个方法是当前可见的;如果这个类也在package a 中,那么noneMethod 是可见的;在任何情况下,类外部实例化出来的对象均无法调用私有方法,比如本示例中的privateMethod 方法。

3.jpg4.jpg

图2-3 方法可见范围示例代码

Java 中的访问权限包括四个等级,权限控制严格程度由低到高,如表2-2 所示。

表2-2 访问权限控制及可见范围

5.jpg

    • public:可以修饰外部类、属性、方法,表示公开的、无限制的,是访问限制最松的一级,被其修饰的类、属性和方法不仅可以被包内访问,还可以跨类、跨包访问,甚至允许跨工程访问。
    • protected:只能修饰属性和方法,表示受保护的、有限制的,被其修饰的属 性和方法能被包内及包外子类访问,但不能被子类对象直接访问。注意,即使并非继承关系,protected 属 性和方法在同一包内也是可见的。
    • 无:即无任何访问权限控制符,如示例中的 noneMethod 方法,没有任何修饰符。千万不要说成default,它并非访问权限控制符的关键字,另外,在JDK8 接口中引入default 默认方法实现,更加容易混淆两者释义。无访问权限控制符仅对包内可见。虽然无访问权限控制符还可以修饰外部类,但是定义外部类极少使用无控制符的方式,要么定义为内部类,功能内聚;要么定义公开类,即public class,包外也可以实例化。
    • private:只能修饰属性、方法、内部类。表示“私有的”,是访问限制最严格的一级,被其修饰的属性或方法只能在该类内部访问,子类、包内均不能访问,更不允许跨包访问。

由此可见,不同的访问权限控制符对应的可见范围不同。在定义类时,要慎重思考该方法、属性、内部类的访问权限,提倡严控访问范围。过于宽泛的访问范围不利于模块间解耦及未来的代码维护。试想,在进行代码重构时,private 方法过旧,我们可以直接删除,且无后顾之忧。可是如果想删除一个public 的方法,是不是要谨慎又谨慎地检查是否被调用。变量就像自己的小孩,要尽量控制在自己的视线范围内,如果作用域太大,无限制地到处乱跑,就会担心其安危。因此,在定义类时,推荐访问控制级别从严处理:

(1) 如果不允许外部直接通过new 创建对象,构造方法必须是private。

(2) 工具类不允许有public 或default 构造方法。

(3) 类非static 成员变量并且与子类共享,必须是protected。

(4) 类非static 成员变量并且仅在本类使用,必须是private。

(5) 类static 成员变量如果仅在本类使用,必须是private。

(6) 若是static 成员变量,必须考虑是否为final。

(7) 类成员方法只供类内部调用,必须是private。

(8) 类成员方法只对继承类公开,那么限制为protected。




版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
阿里云服务器怎么设置密码?怎么停机?怎么重启服务器?
如果在创建实例时没有设置密码,或者密码丢失,您可以在控制台上重新设置实例的登录密码。本文仅描述如何在 ECS 管理控制台上修改实例登录密码。
7744 0
阿里云服务器如何登录?阿里云服务器的三种登录方法
购买阿里云ECS云服务器后如何登录?场景不同,大概有三种登录方式:
2610 0
阿里云服务器端口号设置
阿里云服务器初级使用者可能面临的问题之一. 使用tomcat或者其他服务器软件设置端口号后,比如 一些不是默认的, mysql的 3306, mssql的1433,有时候打不开网页, 原因是没有在ecs安全组去设置这个端口号. 解决: 点击ecs下网络和安全下的安全组 在弹出的安全组中,如果没有就新建安全组,然后点击配置规则 最后如上图点击添加...或快速创建.   have fun!  将编程看作是一门艺术,而不单单是个技术。
9474 0
使用OpenApi弹性释放和设置云服务器ECS释放
云服务器ECS的一个重要特性就是按需创建资源。您可以在业务高峰期按需弹性的自定义规则进行资源创建,在完成业务计算的时候释放资源。本篇将提供几个Tips帮助您更加容易和自动化的完成云服务器的释放和弹性设置。
11586 0
阿里云服务器安全组设置内网互通的方法
虽然0.0.0.0/0使用非常方便,但是发现很多同学使用它来做内网互通,这是有安全风险的,实例有可能会在经典网络被内网IP访问到。下面介绍一下四种安全的内网互联设置方法。 购买前请先:领取阿里云幸运券,有很多优惠,可到下文中领取。
11709 0
腾讯云服务器 设置ngxin + fastdfs +tomcat 开机自启动
在tomcat中新建一个可以启动的 .sh 脚本文件 /usr/local/tomcat7/bin/ export JAVA_HOME=/usr/local/java/jdk7 export PATH=$JAVA_HOME/bin/:$PATH export CLASSPATH=.
4492 0
如何设置阿里云服务器安全组?阿里云安全组规则详细解说
阿里云安全组设置详细图文教程(收藏起来) 阿里云服务器安全组设置规则分享,阿里云服务器安全组如何放行端口设置教程。阿里云会要求客户设置安全组,如果不设置,阿里云会指定默认的安全组。那么,这个安全组是什么呢?顾名思义,就是为了服务器安全设置的。安全组其实就是一个虚拟的防火墙,可以让用户从端口、IP的维度来筛选对应服务器的访问者,从而形成一个云上的安全域。
6535 0
阿里云服务器ECS登录用户名是什么?系统不同默认账号也不同
阿里云服务器Windows系统默认用户名administrator,Linux镜像服务器用户名root
3003 0
+关注
博文视点Broadview
博文视点( Broadview )是电子工业出版社下属旗舰级子公司。在IT出版领域打磨多年,以敏锐眼光、独特视角密切关注技术发展趋势及变化,致力于将技术大师之优秀思想、一线专家之一流经验集结成书,为众多朋友奉献经典著作,助力个人、团队成长。
51
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载