类包
在Java中,我们可以使用class关键字来进行定义一个类,在程序被编译后就会产生一个.class文件。类的定义是规定类名不能重复的,如果程序内只定义了少量的类,那自然不用担心这个问题。
但是,随着类的数量增多,那么难免会出现这个问题,如果根据需求需要定义两个名字相同但是程序逻辑不同的类时,那么该如何解决呢,最好的方法就是将这个两个类放置在不同的包下面。
类的定义规定,在同一个类包下面不允许出来同名的类,但是在不同类包下面就允许出现这种情况,相当于将两个同名的类放入了不同的盒子内,避免出现这个问题。
包和类的关系
类路径
如果一个程序内存在多个同名不同包的max类,那么该如何使用指定包的类呢?
在使用该类时使用该类的类路径,也成为绝对路径。
java.util.Date date=new java.util.Date(); // 选择Java.util包下的Date类
java.sql.Date date2=new java.sql.Date(100) // 选择Java.sql包下的Date类
指定包名(package)
在进行创建包的时候需要注意的是,Java包的命名规则建议使用全小写字母进行命名。
需要了解的是如果一个类没有被定义在一个指定的包内,那么该类就被归纳到默认包(default package)中。
在某个包下面创建一个类的话,需要在这个类的开头加上表达式 package 包名,该表达式需要放置在程序的第一行,使用package关键字指定包名之后,包名也会成为类名的一部分,在不同包下使用该类的话,需要使用 import 包名.类名 的格式。
package test; //指定包名,表示该类在本包内
public class dome{
}
导包
在Java中,如果需要使用到其它包内的类的话,除了使用该类的绝对路径之外,我们还可以使用Java中 import 关键字来指定。例如,如果在其它包内也定义了一个Math,那么假如我们只想使用该Math类的话,那么我们可以使用import关键字进行导包。
例如:
package test;
import Number_5.Math; //导包,导入其它包内的Math类
public class Demo_4 {
public static void main(String[] args) {
int max = Math.max(4, 6); // 进行导包后,再使用时,直接使用包名.类名的方法进行使用
System.out.println(max);
}
}
在进行导包后,我们在使用该包下的Math类后,可以看到,java.lang包下有一个Java给定的Math类,还有一个我们刚刚进行导的包下的Math类。
需要注意的是,刚刚我们已经导入了Number_5包下的Math类,如果需要使用java.lang包下的Math类的话,需要使用java.lang.Math类的完整路径的方式使用该类。因为如果在之前导入了Number_5包下的Math类的话,程序中如果使用Math类中的方法,编译器只会去Number_5.Math类内进行寻找该方法,如果没有找到则报错。
如果需要使用java.lang包下的Math类的话,需要使用全名格式,指定编译器去那个包下寻找min方法。
需要注意的是,如果使用import导入某个包下的所有类时,这些类并不包括该包下的子包的类。如果需要使用子包中的类时,需要对子包进行单独引用。
import 包名.子包名.*; //导入所有类
import 包名.子包名.类名; //导入指定类
导入静态方法或静态成员
使用import关键字还可以对静态方法与静态变量进行导包。
import java.lang.max;
import java.lang.System.out;
public class Demo{
public static void main(String[] args){
out.println("较大值为:"+(max(1,4))); // 导入后可直接使用
}
}
内部类
内部类顾名思义,就定义在一个类内部的类。
成员内部类
定义在一个类成员位置的内部类,称为成员内部类,在成员内部类中可以使用外部类的的任意成员变量与成员方法,即使该外部类成员变量被private所修饰。
package test;
public class Demo_4 {
private int num;
static int i=0;
// 内部类
class test{
public void show(){
// 内部类调用外部类的成员变量
System.out.println(num);
System.out.println(i);
}
}
}
因为private等权限修饰符是针对外界的调用而进行权限判断的,但是成员内部类定义在该类的内部,那么相对于该类的一部分,就像该类的方法也是可以调用被private修饰的成员变量一般,成员内部类属于外部类的成员之一。
内部类是依赖于外部类的存在而存在。除非程序中已经有了一个外部类的对象,否则内部类一般不会单独存在。
内部类也可以被权限修饰符进行修饰,如果内部类被private修饰,那么外界无法创建内部类的对象,只能在外部类中创建内部类的对象。
该如何获取内部类的对象呢?
两种方法:
对外部类编写get方法,对外提供内部类的对象
如果内部类没有被private修饰,那么可以在外界创建一个内部类的对象;
方法二:直接创建内部类对象
成员内部类创建对象格式:
外部类.内部类 类名 = 外部类对象.new 内部类构造方法();
public class Demo_4{
public static void main(String[] args) {
W.N j=new W().new N(); // 链式法创建内部类对象
}
}
// 外部类
class W {
// 内部类
class N {
}
}
方法一:通过get方法对外提供成员内部类对象
这个方法多用于成员内部类被private修饰时,才使用这个方法。
但是如果成员内部类被private修饰了,那么外界也无法使用成员内部类的变量来接收这个返回值,只能使用多态的方式,让成员内部类的父类进行接收,如果该成员内部类没有继承其它类,那么只能使用Object来接收这个返回值
public class Demo_4{
public static void main(String[] args) {
Object a = new W().getN();
}
}
class W {
private class N {
}
public N getN(){
return new N();
}
}
外部类与内部类之间也可使用this关键字,如果在内部类中需要使用到外部类的成员变量,那么可以使用外部类.this.变量名的方式使用。
例如:
public class Demo_4{
public static void main(String[] args) {
W.N a=new W().new N();
a.show();
}
}
class W {
private int i=10;
class N {
private int i=20;
public void show(){
int i=30;
System.out.println(i); // 方法内的i
System.out.println(this.i); // 内部类的i
System.out.println(W.this.i); // 外部类的i
}
}
}
匿名内部类
匿名内部类本质是一个隐藏了名字的内部类。匿名类是只有在创建时才会编写类体的一种写法。
语法格式:
new 继承/实现(){
//重写的方法
};
匿名内部类在一对大括号 { } 的范围内是一个类,整体却是一个new出来的对象。
例如:
public class Demo_4{
public static void main(String[] args) {
// 匿名内部类
new Swit(){
@Override
public void swit(){
System.out.println("重写了swit方法");
}
};
}
}
interface Swit{
public void swit();
}
上述匿名内部类处的语法解读。
整体来说就是,使用空参构造创建一个实现了Swit接口的匿名内部类的对象,就是说,创建匿名内部类的地方是创建了一个对象。
匿名内部类编译后会产生一个以 “外部类名$序号“ 为名称的 .class文件,序号以1-n排列,分别代表1-n个匿名内部类。那么我们可以找到这个.class文件,并对其进行反编译来查看我的结论是否正确。
匿名内部类产生的.class文件
javap 命令,可以对.class文件进行反编译,将字节码文件进行反编译,会打印出该字节码文件包含的信息
反编译的结果:被圈起来的是虚拟机自行添加的空参构造与重写的接口方法。
上述反编译的结果可以说明,使用匿名内部类的格式创建的是一个匿名内部类的对象,而大括号内{}的才是匿名内部类的类体。
匿名内部类的对象如果没有赋值给任何引用变量的话,该对象会在使用后被Java虚拟机自动销毁。
匿名类不能写构造方法。
匿名类不能定义静态的成员。
那么匿名内部类对象可以赋值给那些引用变量呢?可以被赋值给被继承的接口或者父类对象。形成多态。
父类对象调用子类方法
public class Demo_4{
public static void main(String[] args) {
Swit s=new Swit(){
public void swit(){
System.out.println("重写了swit方法");
}
};
s.swit(); // 父类对象调用子类重写的方法
}
}
interface Swit{
public void swit();
}
运行过程
匿名内部类的那个整体是一个对象,只有大括号内的才是那个具体的匿名内部类。