栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

JavaSE - 类的高级特性

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

JavaSE - 类的高级特性

JavaSE - 类的高级特性

本节学习目标:

  • 了解并掌握包的使用方式;
  • 了解并掌握final关键字的使用方式;
  • 了解并掌握内部类的类型与使用方式。
1. 包

为了更好地组织和管理类,Java提供了类包(Package)机制,与C++的命名空间(Namespace)功能类似,Java的包用于确定类的唯一标识。

1.1 包的创建

在Java中包的设计要与系统中文件夹结构相对应,如一个包名为pers.dyj123,那么该包中的类就应位于pers文件夹下的dyj123子文件夹中。
没有定义包名的类将会被归纳为默认包中。在实际开发时应当为所有类都指定包名。在类中定义包名使用package关键字:

package pers.dyj123;
public class MyClass {
    public static int sum(int x, int y) {
        return x + y;
    }
}

package语句必须放在源码的第一行。它必须是源码中的第一行非注释代码,包名会成为类名的一部分。
比如上面例子中的类的完整类名(全限定类名)就为pers.dyj123.MyClass,类名(非限定类名,也叫短类名)为MyClass。

1.2 包的导入

如果要多次使用同一个类,那么就需要写很多次这个类的全限定类名:

public class Test {
    public static void main(String[] args) {
        int x = pers.dyj123.MyClass.sum(10, 20);
        int y = pers.dyj123.MyClass.sum(20, 30);
        int z = pers.dyj123.MyClass.sum(30, 40);
    }
}

如果全限定类名很长的话会导致代码臃肿,可读性变差,影响美观。
为了解决这个问题,Java提供了import关键字,可以导入全限定类名,这样在代码中只使用类名即可指代全限定类名:

import pers.dyj123.MyClass;
public class Test {
    public static void main(String[] args) {
        int x = MyClass.sum(10, 20);
        int y = MyClass.sum(20, 30);
        int z = MyClass.sum(30, 40);
    }
}

如果要使用同一个包下的很多类时,可以使用通配符(*)来导入一个包下的所有类(不包括这个包的子包中的类):

import pers.dyj123.*;
public class Test {
    public static void main(String[] args) {
        int x = MyClass.sum(10, 20);
        int y = MyClass.sum(20, 30);
        int z = MyClass.sum(30, 40);
    }
}

如果导入一个类仅使用这个类中的静态变量、静态常量或静态方法时,
import关键字可以和static关键字联合使用(JDK1.5新特性),用于导入类中的静态属性(需要指定导入的静态属性名或方法名):

import static pers.dyj123.MyClass.sum;
public class Test {
    public static void main(String[] args) {
        int x = sum(10, 20);
        int y = sum(20, 30);
        int z = sum(30, 40);
    }
}

在编写代码时,Java默认会导入java.lang包下的类(不包括其子包下的类),使用这些类时不必使用import关键字导入,直接使用类名即可。

1.3 包名的命名规范
  • 包名命名规范:
    • 包名命名的一般规则:<域名>.<公司名或个人名>.<项目名或工程名>.<模块名>...(如com.sun.net.httpserver.HttpContext);
    • 域名:如org,com,net等,还有一些特殊的域名:
      • indi:个体项目,指个人发起,但非自己独自完成的项目,可公开或私有项目,版权主要属于发起者;
      • pers:个人项目,指个人发起,独自完成,可分享的项目,版权主要属于个人;
      • priv:私有项目,指个人发起,独自完成,非公开的私人使用的项目,版权属于个人;
      • onem:与indi相同,推荐使用indi;
      • team:团队项目,指由团队发起,并由该团队开发的项目,版权属于该团队所有。
    • 项目名或工程名,模块名及其之后的包名统一为小写字母,单词与单词之间紧挨。
2. final 关键字

final这个单词本身意义为“最终的”,“最后的”。当它被用作Java关键字final时,它的意义常常为“最终的”,“不可变”的。

final关键字可以修饰常量,方法和类:

  • final关键字修饰的变量是一个常量;
  • final关键字修饰的方法不能被子类重写;
  • final关键字修饰的类不能被继承。
2.1 final 常量

final关键字可用于变量的声明,当变量被final修饰时,一旦该变量被初始化,就不可以再改变该变量的值。
通常情况下,被final关键字修饰的变量被称为常量(Constant),为了规范,被final修饰的常量名需大写,单词与单词之间使用下划线连接。

常量是不可变的值,比如数学中的圆周率PI(π),它就是个常量,可以使用以下语句定义:

final double PI = 3.1415;

常量在声明时就必须进行初始化(赋值操作),final关键字除了可以修饰基本数据类型常量,也可以修饰引用数据类型常量。
基本数据类型常量被初始化后,就不可以更改它的值,引用数据类型常量被初始化后,就不可以使它指向另一个对象。

在Java中定义全局常量,通常使用public static final修饰,全局常量必须在声明时被初始化(赋值)。

在方法中,final关键字可以修饰方法的参数,被final关键字修饰的参数与常量相同,使用时无法更改它的值,无法更改它指向的地址:

public static int sum(final int x, final int y) {
    return x + y;
}
2.2 final 方法

final关键字也可以修饰方法本身,子类无法重写父类中被final关键字修饰的方法。

编写代码进行测试:

class SuperClass {
    public final void method() {
        System.out.println("这是父类的method()方法");
    }
}
public class SubClass {
    public final void method() {
        System.out.println("这是子类的method()方法");
    }
}

尝试进行编译,Java编译器报错:

2.3 final 类

final关键字甚至可以修饰类,被final关键字修饰的类无法被继承。final关键字无法修饰抽象类与接口。

编写代码进行测试:

final class SuperClass {
}
public class SubClass extends SuperClass {
    
}

尝试进行编译,Java编译器报错:

3. 内部类

类可以定义在类的内部,定义在一个类的内部的类被称为内部类(Inner Class),相对的,包含一个类的类被称为外部类(Outer Class)。

3.1 成员内部类

在一个类中定义成员内部类,分别对外部类和内部类实例化:

class OuterClass {
    public OuterClass() {
        System.out.println("外部类被实例化");
    }
    public class InnerClass {
        public InnerClass() {
            System.out.println("内部类被实例化");
        }
    }
}
public class Test {
    public static void main(String[] args) {
        OuterClass out = new OuterClass();
        OuterClass.InnerClass in = out.new InnerClass();
    }
}

运行结果:

外部类被实例化
内部类被实例化

外部类可以间接访问成员内部类的任何属性和方法:

class OuterClass {
    public OuterClass() {
        InnerClass innerClass = new InnerClass();
        innerClass.method(innerClass.field);
    }
    public class InnerClass {
        private String field = "外部类可以间接访问内部类的属性和方法";
        public void method(String str) {
            System.out.println(str);
        }
    }
}
public class Test {
    public static void main(String[] args) {
        new OuterClass();
    }
}

运行结果:

外部类可以间接访问内部类的属性和方法

成员内部类可以直接访问外部类的任何属性和方法:

class OuterClass {
    private String field = "内部类可以直接访问外部类的属性和方法";
    public void method(String str) {
        System.out.println(str);
    }
    public class InnerClass {
        public InnerClass() {
            method(field); // 内部类对象隐式的在外部保存了一个引用,指向创建它的外部类对象,所以这里不用再创建外部类对象
        }
    }
}
public class Test {
    public static void main(String[] args) {
        OuterClass out = new OuterClass();
        OuterClass.InnerClass in = out.new InnerClass();
    }
}

运行结果:

内部类可以直接访问外部类的属性和方法

如果不想让外部类访问成员内部类的属性和方法,可以将成员内部类使用private关键字修饰:

class OuterClass {
    private class InnerClass {
    }
}
public class Test {
    public static void main(String[] args) {
        OuterClass out = new OuterClass();
        OuterClass.InnerClass in = out.new InnerClass();
    }
}

尝试编译,Java编译器报错:

喝水这一功能,多功能水壶可以以多种方式进行实现,但是类本身只能有一种实现方法,使用内部类可以有多种实现:

interface Drinkable {
    void drink();
}
class MultifunctionalTeaBottle {
    private class Straw implements Drinkable {
        @Override
        public void drink() {
            System.out.println("多功能水壶可以用吸管喝水");
        }
    }
    private class TeaLid implements Drinkable {
        @Override
        public void drink() {
            System.out.println("多功能水壶可以用杯盖喝水");
        }
    }
    public Straw useWithStraw() {
        return new Straw();
    }
    public TeaLid useWithTeaLid() {
        return new TeaLid();
    }
}
public class Test {
    public static void main(String[] args) {
        MultifunctionalTeaBottle bottle = new MultifunctionalTeaBottle();
        Drinkable drinkable1 = bottle.useWithStraw(); // 内部类向上转型为接口
        drinkable1.drink();
        Drinkable drinkable2 = bottle.useWithTeaLid(); // 内部类向上转型为接口
        drinkable2.drink();
    }
}

运行结果:

多功能水壶可以用吸管喝水
多功能水壶可以用杯盖喝水

在内部类中使用this关键字获取外部类的引用:

class OuterClass {
    public int x = 5;
    class InnerClass {
        public int x = 10;
        public void method() {
            System.out.println(this.x);            // 使用内部类的变量x
            System.out.println(OuterClass.this.x); // 使用外部类的变量x
        }
    }
}

成员内部类的特点:

  • 内部类可以用四种访问修饰符修饰,外部类只能修饰为public或缺省;
  • 成员内部类不能声明静态属性或静态方法;
  • 成员内部类可以随意使用外部类的所有成员方法以及成员变量(包括被private关键字修饰的),反之亦然;
  • 如果成员内部类被private关键字修饰,那么外部类无法访问成员内部类的属性和方法。但成员内部类仍然可以访问外部类的属性和方法。
  • 成员内部类的实例化依赖外部类对象的引用。
  • 在内部类中访问自身引用使用this关键字,访问外部类引用则使用外部类类名.this。
3.2 局部内部类

内部类不仅可以在类中定义,也可以在局部位置定义,如方法体中:

class OuterClass {
    public static void method() {
        class InnerClass {
            public InnerClass() {
                System.out.println("内部类定义在方法体中");
            }
        }
        new InnerClass();
    }
}
public class Test {
    public static void main(String[] args) {
        OuterClass.method();
    }
}

运行结果:

内部类定义在方法体中

局部内部类InnerClass是外部类OuterClass的方法method()的一部分,并非外部类OuterClass的一部分。
外部类不能访问局部内部类的属性和方法;局部内部类可以访问外部类的属性和方法,以及当前作用域的常量(可以访问但不能修改,因为是常量)。

3.3 匿名内部类

匿名内部类是一种特殊的内部类,它的作用主要是用来简化抽象类或接口的代码实现。

如果想实现一个接口中的方法,通常需要先定义一个实现这个接口的类,再写实现方法,最后new一个这个类调用此方法:

interface A {
    void method();
}
class AClass implements A {
    @Override
    public void method() {
        // input your code here...
    }
}
public class Test {
    public static void main(String[] args) {
        A a = new AClass();
        a.method();
    }
}

使用匿名内部类可以简化实现方式:

interface A {
    void method();
}
public class Test {
    public static void main(String[] args) {
        A a = new A() {
            @Override
            public void method() {
                // input your code here...
            }
        };
        a.method();
    }
}

在本章3.1节成员内部类中多功能水壶的例子,可以改为如下形式:

interface Drinkable {
    void drink();
}
class MultifunctionalTeaBottle {
    public Drinkable useWithStraw() {
        return new Drinkable() { // 匿名内部类
            @Override
            public void drink() {
                System.out.println("多功能水壶可以用吸管喝水");
            }
        };
    }
    public Drinkable useWithTeaLid() {
        return () -> System.out.println("多功能水壶可以用杯盖喝水"); // 使用lambda表达式进一步简化匿名内部类
    }
}
public class Test {
    public static void main(String[] args) {
        MultifunctionalTeaBottle bottle = new MultifunctionalTeaBottle();
        Drinkable drinkable1 = bottle.useWithStraw(); // 内部类向上转型为接口
        drinkable1.drink();
        Drinkable drinkable2 = bottle.useWithTeaLid(); // 内部类向上转型为接口
        drinkable2.drink();
    }
}

匿名内部类没有名称,它的含义是创建一个实现接口或者抽象类的对象,主要作用是简化实现接口或抽象类的方式。

编译含有匿名内部类的源码文件时,会生成名为“外部类名$序号”的字节码文件,序号1~n对应源码中1~n个匿名内部类。

3.4 静态内部类

使用static关键字修饰成员内部类后,这个内部类就被称为静态内部类。

静态内部类的特点:

  • 静态内部类可以声明静态属性或方法;
  • 静态内部类不可以访问外部类的非静态成员;
  • 静态内部类实例化时不需依赖外部类对象的引用。

编写代码进行测试:

class OuterClass {
    public int x = 5;
    static class InnerClass {
        public static void method() {
            System.out.println(new OuterClass().x);
        }
    }
}

尝试编译,Java编译器报错:

编译含有成员内部类或静态内部类的源码文件时,会生成名为“外部类类名$内部类类名”的字节码文件。

3.5 内部类的继承

内部类可以像外部类一样被继承:

class OuterClass {
    class InnerClass {

    }
}
public class Test extends OuterClass.InnerClass {
    public Test(OuterClass out) {
        out.super();
    }
}

继承内部类的规则:

  • 内部类的子类需要指定继承外部类类名.内部类类名;
  • 继承一个成员内部类时,需要在所有的构造方法中添加一个外部类的引用。继承一个静态内部类则不用;
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/390362.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号