1.1 创建内部类
把类的定义置于外围类的里面:
public class Parcel{
class Contents{
private int i = 11;
public int value(){ return i;}
}
public Contents contents(){
return new Contents();
}
public static void main(String[] args){
Parcel p = new Parcel();
Parcel.Contents c = p.contents();
}
}
其实上使用内部类与使用普通类相比较没有什么不同,但是因为内部类是嵌套在类中的,我们可以在外部类中定义一个方法用来返回内部类中的引用。
如果想从外部类的非静态方法之外的任意位置创建某个内部类的对象,那么必须像在main()方法中那样,具体地指明这个对象的类型:OuterClassName.InnerClassName
1.2 链接到外部类
内部类似乎只是一种名字隐藏和组织代码的模式;当生成一个内部类的对象时,此对象与制造它的外围对象之间就有了一种联系,所以它能访问其外围对象的所有成员,而不需要任何特殊条件。
内部类还拥有其外围类的所有成员的访问权,内部类的对象只能在与其外围类的对象相关联的情况下才能被创建。
构建内部类对象时,需要一个指向其外围类对象的引用,如果编译器访问不到这个引用就会报错。
1.3 生成对外部类对象的引用
如果你需要生成对外部类对象的引用,可以使用外部类的名字后面紧跟 .this。这样产生的引用自动具有正确的类型,这一点在编译期就被知晓并受到检查,因此没有任何运行时开销。
在外部使用内部类的两种方式:
1.3.1 在外围类中定义一个方法,该方法返回内部类的一个对象,方法是public的
public class DotThis{
void f(){
System.out.println(“DotThis.f()”);
}
public class Inner{
public DotThis outer(){
return DotThis.this;
}
}
public Inner inner(){
return new Inner();
}
public static void main(String[] args){
DotThis dt = new DotThis();
DotThis.Inner dti = dt.inner();
dti.outer().f();
}
}
1.3.2 用外围类的对象得到内部类对象
有时候你可能想要告知某些其他对象,去创建其某个内部类的对象。要实现此目的,你必须在new表达式中提供对其他外部类对象的引用,这时需要使用 .new语法:
public class DotNew{
public class Inner{}
public static void main(String[] args){
DotNew dn = new DotNew();
DotNew.Inner dni = dn.new Inner();
}
}
要想直接创建内部类的对象,不能去引用外部类的名字DotNew,而是必须使用外部类的对象来创建该内部类对象。这也解决了内部类名字作用域的问题,因此你不必声明,dn.new DotNew.Inner()。
在拥有外部类对象之前是不可能创建内部类对象的。这是因为内部类对象会暗暗地连接到创建它的外部类对象上。但是,如果你创建的是嵌套类(静态内部类),那么它就不需要对外部类对象的引用。
1.4 内部类与向上转型
当将内部类向上转型为其基类,尤其是转型为一个接口的时候,内部类就有了用武之地。这是因为此内部类–某个接口的实现–能够完全不可见,并且不可用。所得到的只是指向基类或接口的引用,所以能够很方便地隐蔽实现细节。
public interface Destination{
String readLabel();
}
public interface Contents{
int value();
}
现在Contents和Destination表示客户端程序员可用的接口。当取的得了一个指向基类或接口的引用时,甚至可能无法找出它确切的类型:
class Parcel{
private class PContents implements Contents{
private int i =11;
public int value(){return i;}
}
protected class PDestination implements Destination{
private String label;
private PDestination(String whereTo){
label = whereTo;
}
public String readLabel(){ return label;}
}
public Destination destination(String s){
return new PDestination(s);
}
public Contents contents(){
return new PContents();
}
}
public class TestParcel{
public static void main(String[] args){
Parcel p = new Parcel();
Contents c = p.contents();
Destination d = p.destination(“Tasmania”);
}
}
Parcel中增加了一些新东西:内部类PContents是private,所以除了Parcel,没人能访问它。PDestination是protected,所以只有Parcel及其子类、还有与Parcel同一个包中的类能访问PDestination,其他类都不能访问PDestination。这意味着,如果客户端程序员想了解或访问这些成员,那是受限制的。
private内部类给类的设计者提供了一种途径,通过这种方式可以完全组织任何依赖于类型的编码,并且完全隐藏了实现的细节。
此外,从客户端程序员的角度来看,由于不能访问任何新增加的、原本不属于公共接口的方法,所以扩展接口是没有价值的。这也给Java编译器提供了生成更高效代码的机会。
上边已经简单介绍了内部类的使用,除了作为外部类的一个成员存在的内部类(成员内部类
)以外还有其他形式的内部类:局部内部类、匿名内部类
2.1 局部内部类
在Java中,可以在一个方法里面或者在任意的作用域内定义内部类。这么做的两个理由:
-
1.实现了某类型的接口,于是可以创建并返回对其的引用。
-
2.需要解决一个复杂的问题,想创建一个类来辅助解决方案,但是又不希望这个类是公共可用的。
在方法的作用域内创建一个完整的类,这称作局部内部类:
public class Parcel5{
interface Destination{}
public Destination destination(String s){
class PDestination implements Destination {
private String label;
private PDestination(String whereTo) {
label = whereTo;
}
public String readLabel(){return label;}
}
return new PDestination(s);
}
public static void main(String[] args) {
Parcel5 p = new Parcel5();
Destination d = p.destination(“Tasmania”);
}
}
在任意的作用域内嵌入一个内部类:
public class Parcel6 {
private void internalTracking(boolean b){
if(b){
class TrackingSlip{
private String id;
TrackingSlip(String s){
id=s;
}
String getSlip(){return id;}
}
TrackingSlip ts=new TrackingSlip(“slip”);
String s=ts.getSlip();
}
}
public void track(){
internalTracking(true);
}
public static void main(String[] args) {
Parcel6 p=new Parcel6();
p.track();
}
}
当我们需要一个已命名的构造器,或者需要重载构造器时我们会用到局部内部类,而不能使用匿名内部类,因为匿名内部类只能用于实例初始化。当我们需要不止一个该内部类的对象,也要采用局部内部类。
局部内部类不能有访问说明符,因为它不是外围类的一部分;但是它可以访问当前代码块内的常量,以及此外围类的所有成员。局部内部类的名字在方法外是不可见的。
2.2 匿名内部类
public class Parcel {
interface Contents {}
// class MyContents implements Contents {
// private int i=11;
// public int value(){return i;}
// }
public Contents contents() {
return new Contents() {
private int i = 11;
public int value() {return i;}
};
// return new MyContents();
}
public static void main (String[]args){
Parcel p = new Parcel();
Contents c = p.contents();
}
}
创建一个继承自Contents的匿名类的对象,在这个匿名内部类中,使用了默认的构造器来生成Contents。如果定义一个匿名内部类,并且希望它使用一个在其外部定义的对象,那么编译器会要求其参数是final的。否则,编译器将会报错。如果只是传递给匿名类的基类的构造器,那么不需要将传入的形参定为final。(匿名内部类没有构造器,只能用实例化代替。)
2.3 嵌套类
如果不需要内部类对象与其外围类对象之间有联系,那么可以将内部类声明为static。这通常称为嵌套类。普通的内部类对象隐式地保存了一个引用,指向创建它的外围类对象。然而,当内部类是static的时,就会不一样了。嵌套类意味着:
-
1.要创建嵌套类的对象,并不需要其外围类的对象
-
2.不能从嵌套类的对象中访问非静态的外围类对象
嵌套类与普通的内部类还有一个区别,普通内部类的字段与方法,只能放在类的外部层次上,所以普通的内部类不能有static数据和static字段,也不能包含嵌套类。
接口内部的类:
正常情况下,不能在接口内部放置任何代码,但嵌套类可以作为接口的一部分。你放到接口中的任何类都自动地是public和static的。因为类是static的,只是将嵌套类置于接口的命名空间内,这并不违反接口的规则。你甚至可以在内部类中实现其外围接口:
public interface ClassInInterface{
void howdy();
class Test implements ClassInInterface{
public void howdy(){
System.out.print(“Howdy!”);
}
public static void main(String[] args){
new Test().howdy();
}
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Java开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
索成长,自己不成体系的自学效果低效漫长且无助。**
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-v3wmLEd9-1715720121375)]
[外链图片转存中…(img-RFPuiduK-1715720121376)]
[外链图片转存中…(img-LAynqf59-1715720121376)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Java开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!