10.1java类包
10.1.1类名冲突
java编译器将.java的类文件中编译成.class的文件。但是当程序规模太大时,会尝试类名冲突的现象。此时需要将两个同名的类放在不同的类包中。java中每个类或接口都来自确定的类包。
10.1.2完整的类路径
一个完整的类名需要包名和类名的组合,如”java.lang.Math“,java.lang是包名称,Math是类名。
当一个程序中需要使用到同名不同包中的类时,需要在指定代码中给出完整类路径,举例如下:
java.util.Date date=new java.util.Date();
java.sql.Date date2=new java.sql.Date(233);
同一个包中的类互相访问时,可以不指定包名。
同一个包中的类可以放在不同位置,只要将CLASSPATH分别指向两个位置即可。
举例:https://wenku.baidu.com/view/1060b311866fb84ae45c8d1e.html
10.1.3创建包
在类中定义包名语法如下:
package 包名;
java包的命名规则是全部使用小写字母。
java定义包名时通常使用创建者的Internet域名的反序。
package表达式必须是文件的第一行非注释代码,使用该表达式后,包名将成为类名的一部分。注释
<font color>java定义包名时通常使用创建者的Internet域名的反序?</font>
10.1.4导入包
1.使用import关键字导入包
import关键字语法如下:
import com.lzw.*;
//导入自定义包中的所有类,'*'表示所有类
import com.lzw.Math;
//指定com.lzw中的Math类在该程序中可用
//已经导入一个Math类,若再使用其他包中的Math类,必须指出其他Math类的完整类名
使用import导入一个包中所有类时,并不会导入这个包的子包的类
2.使用imort导入静态成员
在jdk5.0以上的版本中,可以使用import导入静态成员,语法如下:
import static 静态成员;
举例如下:
import static java.lang.Math.max; //导入静态成员方法
import static java.lang.System.out; //导入静态成员变量
public class ImportTest{
public static void main(String[] args) {
out.println(max(1,4));
}
}
10.2final常量
final关键词用来声明常量,当常量的值被确定,该值不可再修改,重复定义或修改会报错。
一个对象引用被修饰为final后,只能指向一个恒定的对象,不能再指向其他对象。
一个即是static又是final的字段只占据一段不能改变的存储空间(共享的常量)。
import static java.lang.System.out;
import java.util.Random;
class Test {
int i = 0;
}
public class FinalData {
static Random rand = new Random();
private final int VALUE_1 = 9;
//private static final int VALUE_2=10;
private final Test test = new Test();
private Test test2 = new Test();
private final int[] a = {1, 2, 3, 4, 5, 6};
private final int i4 = rand.nextInt(20);
private static final int i5 = rand.nextInt(20);
public String toString() {
return i4 + " " + i5 + " ";
}
public static void main(String[] args) {
FinalData data = new FinalData();
data.test.i = 1;
//data.test=new Test();错误
//对于指定为final的引用,可以修改引用指向对象的值,但是不能让引用指向另一个对象
data.test2 = new Test();
//正确,可以将非final的引用指向其他对象
//data.VALUE_1++;错误
//不能修改常量的值
data.a[3] = 3;
//final类型的数组,数组引用不可修改,但是若内部数据不是final类型,内部数据可以修改
out.println(data);
out.println("data2");
out.println(new FinalData());
}
}
被定义为final的对象通常用大写字母命名,并且中间使用下划线链接。
被定义为final的基本类型,在初始化后不可改变。
被定义为final的对象引用和数组,final使得引用和数组指向恒定不变,但若指向的内部数据不是final,则内部数据可以改变。
final表明数据为常量,但是仅仅在本对象中值是确定的,另一对象初始化时该常量时可能赋予了其他值。为了实现真正的常量还需要用到static(指明数据只占用一块存储区域)。
import java.util.Random;
import static java.lang.System.out;
public class FinalStaticData {
private static Random rand = new Random();
//实例化一个Randoml类对象
private final int a1 = rand.nextInt(10);
private final static int a2 = rand.nextInt(20);
public static void main(String[] args) {
FinalStaticData data1 = new FinalStaticData();
out.println("data1调用a1的值" + data1.a1);
out.println("data1调用a2的值" + data1.a2);
FinalStaticData data2 = new FinalStaticData();
out.println("data2调用a1的值" + data2.a1);
out.println("data2调用a2的值" + data2.a2);
}
}
在java中定义全局变量,通常使用public static final 修饰。这样的常量必须在定义时被赋值。
10.3final方法
final方法不可以被子类修改,而且final方法执行效率高于非final方法。
private方法无法被子类修改,故一个private方法隐式被定义为final类型。
若父类存在定义为private final的方法,子类可以定义一个同名方法,当是和父类的方法无关,不是覆盖。
举例如下:
class Parents{
private final void doit(){
System.out.println("父类.doit()");
}
final void doit2(){
System.out.println("父类.doit2()");
}
public void doit3(){
System.out.println("父类.doit3()");
}
}
class Sub extends Parents{
public void doit(){
System.out.println("子类.doit()");
}
//final方法不能被覆盖
//final void doit2(){
// System.out.println("子类.doit2()");
//}
public void doit3(){
System.out.println("子类.doit3()");
}
public void doit4(){}
}
public class FinalMethod{
public static void main(String[] args) {
Sub s=new Sub();
s.doit();
Parents p=s;
//错误,private方法仅类内可见,不能被其他类中对象调用
//p.doit();
p.doit2();
p.doit3();
//错误,子类向上转型后,只能调用父类已存在方法,而且父类非final方法可以被覆盖
//p.doit4();
}
}
10.4final类
定义为final的类不允许继承,也不可进行其他修改。语法如下:
final 类名()
final类中的所有方法都被隐式设置为final形式。成员变量则不被限定,可以根据需求定义为final和非final形式。
final class FinalClass{
int a=0;
void doit(){
}
public static void main(String[] args) {
FinalClass f=new FinalClass();
f.a++;
//final类中的成员变量仍是变量,并非常量
System.out.println(f.a);
}
}
10.5内部类
内部类即类内类,可以分为成员内部类、局部内部类、匿名类。
10.5.1成员内部类
1.成员内部类简介
内部类可以随意使用外部类的成员方法和成员变量,即使这些类成员被修饰为private。
内部类实例需要通过外部类实力调用,应该在外部类中实例化一个内部类对象。
public class OuterClass{
innerClass in=new innerClass();
//实例化的内部类对象
public void ouf(){
in.inf();
//外部类方法调用内部类方法
}
class innerClass{
innerClass(){} //内部类构造方法
public void inf(){ //内部类成员方法
}
int y=0;
//内部类成员变量
}
public innerClass doit(){
//y=4;
//外部类不能直接访问内部类成员变量,需要通过内部类对象调用
//该外部类返回一个新的内部类实例
in.y=4;
return new innerClass();
}
public static void main(String args[]){
OuterClass out=new OuterClass();
//内部类的对象实例化操作必须要在外部类或者外部类的非静态方法
OuterClass.innerClass in=out.doit();
OuterClass.innerClass in2=out.new innerClass();
}
}
在主函数中使用内部类,应该通过外部类对象中实例化生成的内部类对象引用,或者外部类提供的调用内部类的方法,或者外部类对象new出来的内部类对象。
内部类和外部类可以交互使用彼此类中定义的变量。
2.内部类向上转型为接口
将一个权限修饰符为private的内部类向上转型为父类对象或者接口,可以在程序中完全隐藏内部类的具体实现过程。
基于此原理,可以一个类中多次实现接口中同一方法:外部提供一个带方法的接口。内部类实现接口的方法,从而定义多个内部类以不同方式实现接口中的同一方法。
interface OutInterface{
//定义的接口和方法
public void f();
}
public class InterfaceInner{
public static void main(String[] args) {
OuterClass2 out =new OuterClass2();
//实例化一个OuterClass2对象,调用doit()函数返回一个OuterInterface内部类
OutInterface outinter=out.doit();
//返回的内部类向上转型为接口,通过接口可以调用内部类中已定义好的方法
outinter.f();
//out.in.f();
}
}
class OuterClass2{
//定义一个内部类实现了接口
//OutInterface in=new InnerClass("访问内部类构造方法");
private class InnerClass implements OutInterface{
InnerClass(String s){//内部类实现方法
System.out.println(s);
}
public void f(){//实现接口中的f()方法
System.out.println("访问内部类中的f()方法");
}
}
public OutInterface doit(){
//内部类为private时,无法通过外部类new内部类,此时需要定义一返回内部类的方法
return new InnerClass("访问内部类构造方法");
}
}
Outerclass2类中定义了一个权限为private的内部类和一个返回接口的doit()方法,内部类实现了接口中的f()方法。此时OuterClass2类外的方法无法访问内部类,故OuterClass2类仅留下了一个外部类和一个接口,通过接口可以调用f()函数,对子类隐藏了f()方法的具体细节。
非内部类不能被声明为private或者protected访问类型。
3.使用this关键字获取内部类与外部类的引用
若遇到内部类对象与外部类对象重名的情况,可以使用this关键字进行区分。
public class TheSameName{
private int x;
private class inner{
private int x;
public void doit(int x){
x++; //调用形参
this.x++; //调用内部类的变量x
TheSameName.this.x++; //调用外部类的变量x
}
}
}
在内存中,所有对象放在堆中,方法以及相关参数放在栈中。
10.5.2局部内部类
内部类也可以在类的局部位置定义,如类的方法或任意作用域中。
interface OutInterface2{
}
public class OuterClass3{
public OutInterface2 doit(final String x){
class InnerClass2 implements OutInterface2{
InnerClass2(String s){
s=x;
System.out.println(s);
}
}
return new InnerClass2("doit");
}
}
doit()方法外部不能访问方法中的内部类,但是内部类可以访问当前代码的常量和此外部类的所有成员。
(不理解,可能已被修复)对于有内部类的方法,含形参时,形参应该被定义为final类型,因为形参的生命周期超过方法的生命周期。
10.5.3匿名内部类
该类可以用来创建一个实现接口/抽象类的匿名类对象。
使用方法,在类内方法的开始位置返回一个接口的引用,在return语句中插入一个定义内部类的代码。
interface OutInterface2 {
}
class OuterClass4 {
public OutInterface2 doit() {
return new OutInterface2() {
private int i = 0;
public int getValue() {
return i;
}
};
}
}
匿名类没有类名,使用默认构造方法生成OutInterface2对象。匿名内部类结束后,需要加分号标识,代表创建OuterInterface2引用表达式的标识。
匿名类所有实现代码都需要在大括号之间编写:
return new A{
//内部类体
}
//A为返回的接口或者抽象类。
匿名内部类编译后,会产生“外部类名$序号”为名称的.class文件,序号以1~n排列,表示1~n个匿名内部类。
10.5.4静态内部类
内部类前面加上static就变成了静态内部类。
静态类内可以声明static成员,非静态类不能声明静态成员。
静态内部类对象不能访问非静态外部类对象。
public class StaticInnerClass {
int x = 100;
static class Inner {
void doitInner() {
//System.out.println(x);
//错误,静态内部类无法调用外部类对象
}
public static void main(String args[]) {
System.out.println("a");
}
}
}
在进行软件测试时,如果在每一个java文件中都设置一个主方法,将会出现很多额外代码,此时可以将主方法写入静态类中。以上述代码为例,该类编译时,将生成一个名称为StaticInner$Inner的独立类(内部类)和一个StaticInnerClass类。测试时使用内部类即可完成测试。将所有.class文件打包时,删除编译后的内部类即可。
10.5.5内部类的继承
内部类可以被继承,但是语法相对复杂,举例如下:
public class OutputInnerClass extends ClassA.ClassB {
public OutputInnerClass(ClassA a) {
a.super();
//super是一个指向父类的指针,此处super()调用了父类的构造函数
}
}
class ClassA {
class ClassB {
}
}
需要继承内部类时,必须手写子类的带一个参数的构造方法,参数为内部类对应外部类的引用,并且构造方法中要通过外部类的引用调用super()方法,以提供必要的对象引用。
参考资料:
《java从入门到精通》
[注释] 如com.lzw包中的Math类,完整类名时com.lzw.Math。'
[^] &amp;amp;#39;