10.类的高级特性

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()方法,以提供必要的对象引用。

 

 

 

参考资料:

final使用参考

《java从入门到精通》

 

[注释]  如com.lzw包中的Math类,完整类名时com.lzw.Math。'

[^]  &amp;amp;amp;#39;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值