文章目录
1. Java 类包
1.1 类名冲突
Java中每个接口或类都来自不同的类包,无论是Java API中的类与接口还是自定义的类与接口都需要隶属某一个类包,这个类包包含了一些类和接口。如果没有包的存在,管理程序中的类名称也是一件非常麻烦的事情,如果程序只由一个类定义组成,那么这并不会给程序带来什么影响,但是随着程序代码的增多,难免会出现类同名的问题。例如,在程序中定义一个Login类,而因业务需要,还要定义一个名称为Login的类,但是这两个类所实现的业务完全不同,于是问题就产生了,编译器不会允许存在同名的类文件。解决这类问题的办法是将这两个类放置在不同的类包中。
1.2 完整的类路径
一个完整的类名需要包名与类名的组合,任何一个类都隶属于一个类包,只要保证同一类包中的类不同名,就可以有效地避免同名类冲突的情况。
1.3 创建包
语法如下:
package 包名
在类中指定包名时,需要将package表达式放置在程序的第一行,它必须是文件中的第一行非注释代码。
使用package关键字为类指定包名之后,包名将会成为类名中的一部分,预示着这个类必须指定全名。
注意:Java包的命名规则是全部使用小写字母。
1.4 导入包
1.4.1 使用import关键字导入包
使用ctrl+shift+o,快速导入所有需要的包。
1.4.2 使用import导入静态成员
2. final 变量
final
关键字可用于变量声明,一旦该变量被设定,就不可以再改变该变量的值。通常,由final定义的变量为常量。
final
关键字定义的变量必须在声明时对其进行赋值操作。final
除了可以修饰基本数据类型的常量,还可以修饰对象引用。由于数组也可以被看作一个对象来引用,所以final
可以修饰数组。一旦一个对象引用被修饰为final
后,它只能恒定指向一个对象,无法将其改变以指向另一个对象。一个既是static
又是final
的字段只占据一段不能改变的储存空间。
被定义为final
的常量定义时需要使用大写字母命名,并且中间使用下划线进行连接,这是Java中的编码规则。同时,定义为final
的数据无论是常量、对象引用还是数组,在主函数中都不可以被改变。
import java.util.Random;
public class FinalStaticData {
private static Random rand = new Random();
private final int a1 = rand.nextInt(10);
private final static int a2 = rand.nextInt(10);
public static void main(String[] args) {
FinalStaticData fdata = new FinalStaticData();
System.out.println("重新实例化对象调用a1的值:"+fdata.a1);
System.out.println("重新实例化对象调用a2的值:"+fdata.a2);
FinalStaticData fdata2 = new FinalStaticData();
System.out.println("重新实例化对象调用a1的值:"+fdata2.a1);
System.out.println("重新实例化对象调用a2的值:"+fdata2.a2);
}
}
得到结果:
重新实例化对象调用a1的值:4
重新实例化对象调用a2的值:9
重新实例化对象调用a1的值:6
重新实例化对象调用a2的值:9
可以看出,定义为final
的常量(a1)不是恒定不变的,但是如果声明为static final
形式(a2),就在内存中开辟了一个恒定不变的区域。因此,在Java中定义全局常量,通常使用public static final
修饰,这样的常量只能在定义时被赋值。
3. final 方法
定义为final
的方法不能被重写。
将方法定义为final
l类型可以防止任何子类修改该类的定义与实现方式,同时定义为final
的方法执行效率要高于非final方法。在修饰权限中曾经提到过private
修饰符,如果一个父类的某个方法被设置为private
修饰符,子类将无法访问该方法,自然无法覆盖该方法,所以一个定义为private
的方法隐式被指定为final
类型,这样无须将一个定义为private
的方法再定义为final
类型。
4. final 类
定义为final
的类不能被继承。如果希望一个类不被任何类继承,并且不允许其他人对这个类进行任何改动,可以将这个类设置为final
形式。语法如下:
final 类名{}
如果将某个类设置为final
形式,则类中的所有方法都被隐式设置为final
形式,但是final
类中的成员变量可以被定义为final
或非final
形式。
final class FinalClass {
int a = 3;
void doit() {
}
public static void main(String[] args) {
FinalClass f = new FinalClass();
f.a++;
System.out.println(f.a);
}
}
结果:4
5. 内部类
在类体里面定义一个类,这个类就是内部类。内部类可分为:成员内部类、局部内部类、静态内部类以及匿名内部类。
5.1 成员内部类
5.1.1 成员内部类简介
在一个类中使用内部类,可以在内部类中直接存取其所在类的私有成员变量。成员内部类语法如下:
public class OuterClass {
private class Innerclass{
//...
}
}
在内部类中可以随意使用外部类的成员方法以及成员变量,尽管这些类成员被修饰为private
。内部类的实例一定要绑定在外部类的实例上,如果从外部类中初始化一个内部类对象,那么内部类对象就会绑定在外部类对象上。内部类初始化方式与其他类初始化方式相同,都是使用new
关键字。
public class OuterClass {
innerClass in = new innerClass();
public void outf() {
in.inf();
}
private class innerClass {// 成员内部类
int y = 0;
public innerClass() {
}
public void inf() {
System.out.println("内部类方法y=" + y);
}
}
public static void main(String[] args) {
OuterClass d = new OuterClass();
d.outf();
OuterClass.innerClass in = d.new innerClass(); // 实例化内部类对象
in.inf();
}
}
结果:
内部类方法y=0
内部类方法y=0
内部类可以访问它的外部类成员,但内部类的成员只有在内部类的范围内是可知的,不能被外部类使用。
注意:如果在外部类和非静态方法之外实例化内部类对象,需要使用外部类。内部类的形式指定该对象的类型。
内部类对象会依赖于外部类对象,除非已经存在一个外部类对象,否则类中不会出现内部类对象。
5.1.1 内部类向上转型为接口
如果将一个权限修饰符为private
的内部类向上转型为其父类对象,或者直接向上转型为一个接口,在程序中就可以完全隐藏内部类的具体实现过程。可以在外部提供一个接口,在接口中声明中一个方法。
interface OutInterface {// 定义一个接口
public void f();
}
public class InterfaceInner {
public static void main(String args[]) {
OuterClass2 out = new OuterClass2();// 实例化一个OuterClass2对象
// 调用doit()方法,返回一个OutInterface接口
OutInterface outinter = out.doit();
outinter.f();
}
}
class OuterClass2 {
// 定义一个内部类实现OuterInterface接口
private class InnerClass implements OutInterface {
InnerClass(String s) {// 内部类构造方法
System.out.println(s);
}
public void f() {// 实现接口中的f()方法
System.out.println("访问内部类中的f()方法");
}
}
public OutInterface doit() {
return new InnerClass("访问内部类构造方法");
}
}
结果如下:
内部类方法y=0
内部类方法y=0
注意:非内部类不能被声明为private或protected访问类型。
5.1.3 使用this关键字获取内部类与外部类的引用
如果在外部类中定义的成员变量与内部类的成员变量名称相同,可以使用this
关键字。
例子:
public class TheSameName {
private int x;
private class Inner {
private int x = 9;
public void doit(int x) {
x++;// 调用的是形参x
this.x++;// 调用内部类的变量x
TheSameName.this.x++;// 调用外部类的变量x
}
}
}
在内部类中使用this.x
语句可以调用内部类的成员变量x,而使用TheSameName.this.x
语句可以调用外部类的成员变量x,即使用外部类名称后跟一个点操作符和this
关键字变可以获取外部类的一个引用。
5.2 局部内部类
局部内部类就是在类的方法中定义的内部类,它的作用范围也是在这个方法体内。
public class Demo {
public OutInterface action(String x) {
class innerClass2 implements OutInterface {
public innerClass2(String s) {
s = x;
System.out.println(s);
}
}
return new innerClass2("do");
}
public static void main(String[] args) {
Demo d = new Demo();
d.action("局部内部类");
}
}
interface OutInterface {
}
结果
局部内部类
5.3 匿名内部类
语法如下:
return new A(){
...//内部类体
};
其中,A指类名。在匿名内部类定义结束后,需要加分号进行标识,这个分号不代表内部类的结束,而是代表A引用表达式的创建。
实质上,这种内部类的作用就是创建一个实现与A接口的匿名类的对象。
5.4 静态内部类
在内部类前添加修饰符static,这个内部类就变为静态内部类。一个静态内部类中可以声明static成员,但是在非静态内部类中不可以声明静态成员。静态内部类有一个最大的特点,就是不可以使用外部类的非静态成员,所以静态内部类在程序开发中比较少见。
可以这样认为,普通的内部类对象隐式地在外部保存了一个引用,指向创建它的外部类对象,但如果内部类被定义为static时,它应该具有更多的限制。静态内部类具有以下两个特点:
- 创建静态内部类的对象,不需要其外部类的对象。
- 不能从静态内部类的对象中访问非静态外部类的对象。
public class Demo {
int x = 100;
static class innerClass4 {
public static void main(String[] args) {// 可以在静态内部类中运行main方法
System.out.println("我是静态内部类");
}
}
}
5.5 内部类的继承
内部类也和其他普通类一样可以被继承,但是继承内部类比继承普通类要复杂一些,需要设置专门的语法进行继承。
public class Demo {
class innerClass{// 成员内部类
}
public static void main(String[] args) {
}
}
class NewClass extends Demo.innerClass{
public NewClass(Demo d) {
d.super();
}
}
在某个类继承内部类时,必须硬性给予这个类一个带参数的构造方法,并且该构造方法的参数为需要继承内部类的外部类的引用,同时在构造方法中使用a.super()
语句,这样才为继承提供了必要的引用对象。