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

it2025-01-16  5

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引入的概念 廖雪峰老师关于模块的讲解

最新回复(0)