第十一章-类的高级特性

11.1java类包

在java中定义好一个类,通过java编译器进行编译后,都会生成一个扩展名为.class的文件,当程序的规模逐渐扩大时,很容易发生类名冲突的现象。java中提供了一种管理类文件的机制,就是类包。

11.1.1类名冲突 

java中的每个接口或类都来自不同的类包,无论是java API中的类与接口还是自定义的类与接口都需要隶属于某一个类包,这个类包包含了一些类和接口。如果没有包的存在,管理程序中的类名称将是一件非常麻烦的事,难免会出现类同名的问题,解决这个问题的办法就是将同名的类放在不同的包中。

11.1.2完整的类路径 

一个完整的类名需要包名和类名的组合,每一个类都隶属于一个类包,只要保证同一类包中的类不同名,就可以有效避免同名类冲突的情况。

同一个包中的类相互访问时,可以不指定包;同一个包中的类不必放在同一个位置,如com.lzw.class1和com.lzw.class2可以放在不同的盘中,只要将CLASSPATH分别指向这两个位置即可。

11.1.3创建包

在类中定义包名的语法如下:

在类中指定包名时需要将package表达式放置在程序的第一行,它必须是文件中的第一行非注释代码。当使用package关键字为类指定包名后,包名就成为类名的一部分,预示着这个类必须指定全名。例如,在使用位于com.lzw包下的Dog.java类时,需要使用形如com.lzw.Dog这样的表达式。

注意,java包的命名规则是全部使用小写字母,而类的首字母大写,其余小写。

11.1.4导入包

(1) 使用import关键字导入包

import关键字的语法如下:

在使用import关键字时,可以指定类的完整描述;如果为了使用包中更多的类,可以在使用import关键字指定时在包指定后加上*,这表示可以在程序中使用包中的所有类。但是这并不会指定这个包的子包的类,如果用到子包的类,需要再次对子包进行导入。

(2)使用import导入静态成员

import除了导入包之外,还可以导入静态成员。语法格式如下:

11.2 final变量

由final关键字定义的变量为常量,一旦该变量被设定,就不可以再改变该变量的值(初始化后就不能改变)。例如在类中定义PI值,可以使用如下语句:

当在程序中用到PI这个常量时,它的值就是3.14.如果在程序中再次对定义为final的常量赋值,编译器将不会接受。

final关键字定义的变量必须在声明时对其进行赋值。final除了可以修饰基本数据类型的常量,还可以修饰对象引用。一旦一个对象引用被final修饰后,它就恒定指向一个对象,无法将其改变指向另一个对象。一个既是static又是final的字段占据一段不能改变的存储空间。

在本实例中,被定义为final的常量定义时需要使用大写字母命名,并且中间使用下划线进行连接,这是java中的规则。同时,定义为final的常量、对象引用还是数组,在主函数中都不可以改变。

一个被定义为final的对象引用只能指向唯一的对象(地址不可变),不可以将它再指向其他的对象,但是一个对象的成员变量和成员方法是可以改变的。为了使一个常量真正做到初始化之后就不能改变,可以将常量定义为static final。看下面例子:

 

定义为final的常量不是恒定不变的,将随机数赋予final常量,可以做到每次运行程序时改变a1的值。但是a2由于被定义为static final 形式,所以在内存中为a2开辟了一块恒定不变的区域,当再次实例化一个对象时,任然指向a2这块区域,所以a2的值保持不变。a2是在装载时被初始化,不是每次创建新对象时初始化;而a1的值会在重新实例化对象时因为初始化而改变。

在java中定义全局变量,通常使用public static final修饰,这样的常量只能在定义时被赋值。

同时也可以将方法的参数定义为final,这意味着无法在方法中更改参数引用所指向的对象。

最后总结在程序中final可以出现的位置:

11.3 final方法

将方法定义为final类型可以防止子类修改该方法,所以定义为final的方法不能被重写; 在权限修饰中提到过private修饰符,如果父类的某个方法被private修饰,那么子类将无法访问该方法,所以定义为private的方法隐式被指定为final,这样无需再将一个定义为private的方法再用final修饰。如下例:

上例中,首先必须明确声明为private的父类方法不允许被子类访问,所以在Sub类中只是重写了一个doit()方法,而覆盖的就只有doit3()方法。

11.4 final类

定义为final的类不能被继承,但是存在父类。final类的语法格式:

 final类的中的所有方法都被隐式设置为final形式,但是final类中的成员变量可以被定义为final或者非final形式。

11.5 内部类

 前面学习过在一个包中定义两个类,但是其中任何一个类都不在另一个类的内部。而如果在类中再定义另一个类,那么将这个在类中定义的类称为内部类。内部类可分为成员内部类、局部内部类以及匿名类。

11.5.1 成员内部类

(1)成员内部类简介

在一个类中使用内部类,可以在内部类中直接存取其所在类的私有成员变量 。成员内部类的语法如下:

在内部类中可以随便使用外部类的成员方法以及成员变量,就算这些类成员被修饰为private。

内部类的实例一定要绑定在外部类的实例上,如果从外部类中初始化一个内部类对象,那么内部类对象就会绑定在外部类对象上。内部类的初始化方式与普通类相同,都是使用new关键字。

内部类可以随意访问它的外部类的成员,但是内部类的成员只有在内部类的范围内是可知的,不能被外部类使用,但是可以使用内部类的对象调用内部类的成员,如上例。

此外,内部类的对象实例化操作必须在外部类或外部类的非静态方法中实现。如果在外部类和非静态方法之外实例化内部类对象,则需要使用外部类.内部类的形式指定该对象类型。

上例可以看出,实例化内部类对象时,不能在new操作符之前使用外部类名称实例化内部类对象,而是应该使用外部类对象创建内部类对象。内部类对象完全依赖于外部类对象,除非已经存在一个外部类对象,否则类中不会出现内部类对象。

(2)内部类向上转型为接口

如果将一个权限修饰符为private的内部类向上转型为其父类对象,或者转型为一个接口,在程序中就可以完全隐藏内部类的实现过程。可以在外部提供一个接口,在接口中声明一个方法,如果在实现该接口的内部类中实现该接口的方法,就可以定义多个内部类以不同的方式实现接口中的同一个方法,而在一般的类中是不能多次实现接口中的同一个方法的。看下例:

注意,非内部类不能被声明为private或protected访问类型。

(3)使用this关键字获取内部类或外部类的引用

如果在外部类定义的成员变量与内部类的成员变量的名称相同,可以使用this关键字区分。

应该明确一点,在内存中所有的对象都被放在堆中,方法及方法的形参以及局部变量放在栈中。在下图中,栈中的doit()方法指向内部类的对象,而内部类的对象与外部类的对象是相互依赖的。

11.5.2局部内部类

内部类不仅可以再在类中 定义,也可以在类的局部位置定义,比如类的方法或任意的作用域中都可以定义内部类。

有一点值得注意的是,局部内部类是方法的一部分,并非外部类的一部分,所以在方法的外部都不能访问该内部类,但是该内部类可以访问当前代码块的常量以及此外部类的所有成员。

还有一点,在方法中定义的局部内部类只能访问方法中的final类型的局部变量,这是因为在方法中定义的局部变量相当于一个常量,它的声明周期超出方法运行的生命周期。由于该局部变量被设置为final,所以不能在内部类中改变该局部变量的值。

11.5.3 匿名内部类

匿名类说到底就是类的对象的名称不存在,一般的类的对象例化格式为: 类名 对象名=new 类名(参数);匿名内部类没有了等号的左边部分。匿名类的所有实现代码都需要在大括号之间进行编写,语法如下:

最后的分号代表创建对象表达式的标识。

11.5.4静态内部类

11.5.5内部类的继承 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值