Java笔记五——包、作用域、内部类、classpath和jar、模块

Java使用package解决类名冲突
一个类总是属于某个包,真正的完整类名是包名.类名
在定义class的时候,我们需要在第一行声明这个class属于哪个包。

小明的Person.java文件:

package ming; // 申明包名ming

public class Person {
}

包没有父子关系。java.util和java.util.zip是不同的包,两者没有任何继承关系
没有定义包名的class,它使用的是默认包,非常容易引起名字冲突,因此,不推荐不写包名的做法。
按照包结构把java文件组织起来,例如(ming,hong,mr.jun都是包)
在这里插入图片描述
所有的Java文件对应的目录层次要和包的层次一致
编译后的.class文件也需要按照包结构存放。如果使用IDE,把编译后的.class文件放到bin目录下,那么,编译的文件结构就是:
在这里插入图片描述
编译的命令相对比较复杂,我们需要在src目录下执行javac命令:
javac -d ../bin ming/Person.java hong/Person.java mr/jun/Arrays.java
在IDE中,会自动根据包结构编译所有Java源码,所以不必担心使用命令行编译的复杂命令。

包作用域

位于同一个包的类,可以访问包作用域的字段和方法。不用public、protected、private修饰的字段和方法就是包作用域。例如,Person类定义在hello包下面:

package hello;

public class Person {
    // 包作用域:
    void hello() {
        System.out.println("Hello!");
    }
}

Main类也定义在hello包下面

package hello;

public class Main {
    public static void main(String[] args) {
        Person p = new Person();
        p.hello(); // 可以调用,因为Main和Person在同一个包
    }
}

在一个class中引用其他的class的方法:

  • 直接写出完整类名(太麻烦不推荐)
// Person.java
package ming;

public class Person {
    public void run() {
        mr.jun.Arrays arrays = new mr.jun.Arrays();
    }
}
  • 使用import语句
// Person.java
package ming;

// 导入完整类名:
import mr.jun.Arrays;

public class Person {
    public void run() {
        Arrays arrays = new Arrays();
    }
}

可以使用import mr.jun.* 导入mr.jun包的所有class,但是不推荐
寻找class
Java编译器最终编译出的.class文件只使用完整类名,因此,在代码中,当编译器遇到一个class名称时:
如果是完整类名,就直接根据完整类名查找这个class;
如果是简单类名,按下面的顺序依次查找:
查找当前package是否存在这个class;
查找import的包是否包含这个class;
查找java.lang包是否包含这个class。
如果按照上面的规则还无法确定类名,则编译报错。

编写class的时候,编译器会自动帮我们做两个import动作:
默认自动import当前package的其他class;
默认自动import java.lang.*。
一个工程项目的文件结构
在这里插入图片描述
注意,包名必须完全一致,包没有父子关系,com.apache和com.apache.abc是不同的包。

最佳实践

如果不确定是否需要public,就不声明为public,即尽可能少地暴露对外的字段和方法。

把方法定义为package权限有助于测试,因为测试类和被测试类只要位于同一个package,测试代码就可以访问被测试类的package权限方法。

一个.java文件只能包含一个public类,但可以包含多个非public类。如果有public类,文件名必须和public类的名字相同。

小结

Java内建的访问权限包括public、protected、private和package权限;

Java在方法内部定义的变量是局部变量,局部变量的作用域从变量声明开始,到一个块结束;

final修饰符不是访问权限,它可以修饰class、field和method;

一个.java文件只能包含一个public类,但可以包含多个非public类。

修饰符限定访问作用域

修饰类的成员

public、protected、private三者修饰的类的成员访问作用域如下

上表中的默认就是指没有修饰符修饰

修饰类、接口

定义为public的class、interface可以被其他任何类访问:(导包上面说过了,class和interface一般都定义成public,不然干啥???)

嵌套类(内部类)

定义在一个class内部的class称为嵌套类(nested class),Java支持好几种嵌套类。

内部类概述
  • 把类定义在其他类的内部,就叫做嵌套类
  • 内部类可以访问外部类的field和method,包括private
  • 外部类要访问内部类的成员,必须先创建对象(但是一般内部类就是不想让外部访问,比如body和heart)
    格式:外部类名.内部类名 对象名 = 外部类对象.内部类对象;
    加入定义的Outer是一个普通类,而Inner是一个Inner Class,它与普通类有个最大的不同,就是Inner Class的实例不能单独存在,必须依附于一个Outer Class的实例。示例代码如下:
public class Main {
    public static void main(String[] args) {
        Outer outer = new Outer("Nested"); // 实例化一个Outer
        Outer.Inner inner = outer.new Inner(); // 实例化一个Inner
        inner.hello();
    }
}

class Outer {
    private String name;

    Outer(String name) {
        this.name = name;
    }

    class Inner {
        void hello() {
            System.out.println("Hello, " + Outer.this.name);
        }
    }
}

Inner Class除了有一个this指向它自己,还隐含地持有一个Outer Class实例,可以用Outer.this访问这个实例。所以,实例化一个Inner Class不能脱离Outer实例。

内部类位置
  • 成员位置:成员内部类
  • 局部位置(内部类定义在外部类某个方法中的局部变量):局部内部类
成员内部类
  • 外界创建对象(实际开发一般不创建,内部类就是不想外部访问)
    格式:外部类名.内部类名 对象名 = 外部类对象.内部类对象;
  • 成员内部的常见修饰符
    private为了保证数据的安全性
    static为了让数据访问更方便
     被静态修饰的成员内部类智能访问外部类的静态成员
     内部类的方法
      非静态方法
        外部类名.内部类名 对象名 = new 外部类名.内部类名();
      静态方法
        上面创建的对象访问;或者外部类名.内部类名.方法名();

下面的程序在控制台分别输出:30,20,10
在这里插入图片描述

局部内部类
  • 可以直接访问外部类的成员(很容易理解,和外部类方法差不多)
  • 可以创建内部类对象,通过对象调用内部类方法
    看下面的例子
    在这里插入图片描述

匿名类(Anonymous Class)

匿名类也是一种定义Inner class的方法,且不需要再Outer class中明确定义这个class,而是在方法内部,通过匿名类来定义,示例代码如下

public class Main {
    public static void main(String[] args) {
        Outer outer = new Outer("Nested");
        outer.asyncHello();
    }
}

class Outer {
    private String name;

    Outer(String name) {
        this.name = name;
    }

    void asyncHello() {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                System.out.println("Hello, " + Outer.this.name);
            }
        };
        new Thread(r).start();
    }
}

观察asyncHello()方法,我们在方法内部实例化了一个Runnable。Runnable本身是接口,接口是不能实例化的,所以这里实际上是定义了一个实现了Runnable接口的匿名类,并且通过new实例化该匿名类,然后转型为Runnable。在定义匿名类的时候就必须实例化它,定义匿名类的写法如下:
Runnable r = new Runnable() {
// 实现必要的抽象方法…
};
匿名类和Inner Class一样,可以访问Outer Class的private字段和方法。之所以我们要定义匿名类,是因为在这里我们通常不关心类名,比直接定义Inner Class可以少写很多代码。

静态内部类

public class Main {
    public static void main(String[] args) {
        Outer.StaticNested sn = new Outer.StaticNested();
        sn.hello();
    }
}

class Outer {
    private static String NAME = "OUTER";

    private String name;

    Outer(String name) {
        this.name = name;
    }

    static class StaticNested {
        void hello() {
            System.out.println("Hello, " + Outer.NAME);
        }
    }
}

用static修饰的内部类和Inner Class有很大的不同,它不再依附于Outer的实例,而是一个完全独立的类,因此无法引用Outer.this,但它可以访问Outer的private静态字段和静态方法。如果把StaticNested移到Outer之外,就失去了访问private的权限。

小结
Java的内部类可分为Inner Class、Anonymous Class和Static Nested Class三种:
Inner Class和Anonymous Class本质上是相同的,都必须依附于Outer Class的实例,即隐含地持有Outer.this实例,并拥有Outer Class的private访问权限;
Static Nested Class是独立类,但拥有Outer Class的private访问权限。

classpath和jar

不要设置classpath!对大多数情况编译器能够找到class路径,IDE也可以帮助解决,不必多此一举。

在大型项目中,不可能手动编写MANIFEST.MF文件,再手动创建zip包。Java社区提供了大量的开源构建工具,例如Maven,可以非常方便地创建jar包。

小结

JVM通过环境变量classpath决定搜索class的路径和顺序;

不推荐设置系统环境变量classpath,始终建议通过-cp命令传入;

jar包相当于目录,可以包含很多.class文件,方便下载和使用;

MANIFEST.MF文件可以提供jar包的信息,如Main-Class,这样可以直接运行jar包。

模块

Java 9引入的概念
廖雪峰老师关于模块的讲解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值