一.内部类
- 内部类提供了更好的封装,只有外部类能访问内部类
- 内部类可以独立继承一个接口,不受外部类是否继承接口影响
- 内部类中的属性和方法即使是外部类也不能直接访问,相反内部类可以直接访问外部类的属性和方法,即使private
- 利于回调函数的编写
例子:
public class OuterClass { private String outerName; private int outerAge; public class InnerClass{ private String innerName; private int innerAge; } }
内部类与外部类的联系
内部类可以直接访问外部类的元素,但是外部类不可以直接访问内部类的元素
public class OuterClass { private String outerName; private int outerAge; public class InnerClass{ private int innerName; InnerClass(){ //内部类可以访问外部类的元素 outerName="I am outer class"; outerAge=23; } public void display(){ System.out.println(outerName+" and my age is "+outerAge); } } public static void main(String[] args) { OuterClass outerClass = new OuterClass(); OuterClass.InnerClass innerClass = outerClass.new InnerClass(); innerClass.display(); } }
内部类可以直接访问外部类属性,尽管外部类属性是用private修饰的。这是因为在创建外部类时,内部类会自动捕获一个外部类的引用,所以内部类访问外部类元素,实际上是通过他所持有外部类引用访问的。在java中,我们可以通过OuterClass.this
来获得外部类的引用:
public class OuterClass { public void display(){ System.out.println("this is OuterClass..."); } public class InnerClass{ //获取外部类的引用 public OuterClass getOuterClass(){ return OuterClass.this; } public void innerDisplay(){ //内部类也可以通过外部类的引用访问外部元素 getOuterClass().display(); } } public static void main(String[] args) { OuterClass outerClass = new OuterClass(); OuterClass.InnerClass innerClass = outerClass.new InnerClass(); innerClass.innerDisplay(); } }
外部类可以通过内部类引用间接访问内部类元素:
public class OuterClass { public void display(){ //外部类访问内部类元素,需要通过内部类引用访问 InnerClass innerClass=new InnerClass(); innerClass.innerDisplay(); } public class InnerClass{ public void innerDisplay(){ System.out.println("I am inner class"); } } public static void main(String[] args) { OuterClass outerClass=new OuterClass(); outerClass.display(); } }
创建内部类
在外部类外面(或外部类main方法)创建内部了对象
如果要创建一个内部类对象,必须利用outerClass.new
来创建:
1.
OuterClass outerClass = new OuterClass(); OuterClass.InnerClass innerClass = outerClass.new InnerClass();
2.
OuterClass.InnerClass innerClass=new OuterClass().new InnerClass();
内部类创建方法示例:
public static void main(String[] args) { //先创建外部类对象,再创建内部类对象 OuterClass outerClass = new OuterClass(); OuterClass.InnerClass innerClass1 = outerClass.new InnerClass(); innerClass1.innerDisplay(); //一步到位创建 OuterClass.InnerClass innerClass2=new OuterClass().new InnerClass(); innerClass2.innerDisplay(); }
在外部类里面创建内部类
正如2.3
代码中display()
方法那样,在外部类里面创建内部类,就像创建普通对象一样直接创建:
InnerClass innerClass=new InnerClass()
内部类的种类:
在Java中内部类主要分为成员内部类、方法内部类、匿名内部类、静态内部类。
1 成员内部类
成员内部类也是最普通的内部类,它是外部类的一个成员,所以他是可以无限制的访问外围类的所有成员属性和方法,尽管是private的,但是外围类要访问内部类的成员属性和方法则需要通过内部类实例来访问。
在成员内部类中要注意两点:
- 成员内部类中不能存在任何static的变量和方法
- 成员内部类是依附于外围类的,所以只有先创建了外围类才能够创建内部类
2 方法内部类
方法内部类定义在外部类的方法中,局部内部类和成员内部类基本一致,只是它们的作用域不同,方法内部类只能在该方法中被使用,出了该方法就会失效。 对于这个类的使用主要是应用与解决比较复杂的问题,想创建一个类来辅助我们的解决方案,到那时又不希望这个类是公共可用的,所以就产生了局部内部类。
3 匿名内部类
匿名内部类其实就是一个没有名字的方法内部类,所以它符合方法内部类的所有约束,初次之外,还有一些地方需要注意:
- 匿名内部类是没有访问修饰符的。
- 匿名内部类必须继承一个抽象类或者实现一个接口
- 匿名内部类中不能存在任何静态成员或方法
- 匿名内部类是没有构造方法的,因为它没有类名。
一般使用匿名内部类的场景是,要继承或实现的接口只有一个抽象方法,比如添加一个监听器:
public class Button { public void click(){ //匿名内部类,实现的是ActionListener接口 new ActionListener(){ public void onAction(){ System.out.println("click action..."); } }.onAction(); } //匿名内部类必须继承或实现一个已有的接口 public interface ActionListener{ public void onAction(); } public static void main(String[] args) { Button button=new Button(); button.click(); } }
4 静态内部类
关键字static
可以修饰成员变量、方法、代码块,其实它还可以修饰内部类,使用static
修饰的内部类我们称之为静态内部类。静态内部类与非静态内部类之间存在一个最大的区别,我们知道非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外围内,但是静态内部类却没有。没有这个引用就意味着:
- 静态内部类的创建是不需要依赖于外围类,可以直接创建
-
静态内部类不可以使用任何外围类的非static成员变量和方法,而内部类则都可以
public class OuterClass { private static String outerName; public int age; static class InnerClass1{ /* 在静态内部类中可以存在静态成员 */ public static String _innerName = "static variable"; public void display(){ /* * 静态内部类只能访问外部类的静态成员变量和方法 * 不能访问外部类的非静态成员变量和方法 */ System.out.println("OutClass name :" + outerName); } } class InnerClass2{ /* 非静态内部类中不能存在静态成员 */ public String _innerName = "no static variable"; /* 非静态内部类中可以调用外部类的任何成员,不管是静态的还是非静态的 */ public void display(){ System.out.println("OuterClass name:" + outerName); System.out.println("OuterClass age:" + age); } } public void display(){ /* 外部类能直接访问静态内部类静态元素 */ System.out.println(InnerClass1._innerName); /* 静态内部类可以直接创建实例不需要依赖于外部类 */ new InnerClass1().display(); /* 非静态内部的创建需要依赖于外部类 */ OuterClass.InnerClass2 inner2 = new OuterClass().new InnerClass2(); /* 非静态内部类的成员需要使用非静态内部类的实例访问 */ System.out.println(inner2._innerName); inner2.display(); } public static void main(String[] args) { OuterClass outer = new OuterClass(); outer.display(); } }
二.包的声明与访问
1、包的概念
(1)Java的包,其实就是我们电脑系统中的文件夹,包里存放的是类文件;
(2)当类文件很多的时候,通常我们会采用多个包进行存放管理他们,这种方式称为分包管理;
(3)在项目中,我们将相同功能的类放到一个包中,方便管理。并且日常项目的分工也是以包作为边界;
(4)类中声明的包必须与实际class文件所在的文件夹情况相一致,即类声明在a包下,则生成的.class文件必须在a文件夹下,否则,程序运行时会找不到类。
2、包的声明格式
(1)通常使用公司网址反写,可以有多层包,包名采用全部小写字母,多层包之间用”.”连接;
(2)类中包的声明格式:
package 包名.包名.包名…;
(3)注意:声明包的语句,必须写在程序有效代码的第一行(注释不算)。
(4)代码演示:
package cn.gzdlh; //包的声明,必须在有效代码的第一行 import java.util.Scanner; import java.util.Random; public class Demo {}
3、包的访问
(1)在访问类时,为了能够找到该类,必须使用含有包名的类全名(包名.类名);
(2)当我们要使用一个类时,这个类与当前程序在同一个包中(即同一个文件夹中),或者这个类是java.lang包中的类时通常可以省略掉包名,直接使用该类;
(3)当我们要使用的类,与当前程序不在同一个包中(即不同文件夹中),要访问的类必须用public修饰才可访问;
4、import导包
(1)我们每次使用类时,都需要写很长的包名。很麻烦,我们可以通过import导包的方式来简化,可以通过导包的方式使用该类,可以避免使用全类名编写(即,包类.类名);
(2)导包的格式:
import 包名.类名;
(3)当程序导入指定的包后,使用类时,就可以简化了。演示如下:
1 //导入包前的方式 2 //创建对象 3 java.util.Random r1 = new java.util.Random(); 4 java.util.Random r2 = new java.util.Random(); 5 java.util.Scanner sc1 = new java.util.Scanner(System.in); 6 java.util.Scanner sc2 = new java.util.Scanner(System.in); 7 8 //导入包后的方式 9 import java.util.Random; 10 import java.util.Scanner; 11 //创建对象 12 Random r1 = new Random(); 13 Random r2 = new Random(); 14 Scanner sc1 = new Scanner(System.in); 15 Scanner sc2 = new Scanner(System.in);
(4)import导包代码书写的位置:在声明包package后,定义所有类class前,使用导包import包名.包名.类名;。
三.访问修饰符
对Java中的修饰符一直记不住,现在结合网上的一些资料进行总结,并重点说明一下protected修饰符。
一. Java中的访问修饰符
Java面向对象的基本思想之一是封装细节并且公开接口。Java语言采用访问控制修饰符来控制类及类的方法和变量的访问权限,从而向使用者暴露接口,但隐藏实现细节。访问控制分为四种级别:
(1)public: 用public修饰的类、类属变量及方法,包内及包外的任何类(包括子类和普通类)均可以访问;
(2)protected: 用protected修饰的类、类属变量及方法,包内的任何类及包外那些继承了该类的子类才能访问(此处稍后解释),protected重点突出继承;
(3)default: 如果一个类、类属变量及方法没有用任何修饰符(即没有用public、protected及private中任何一种修饰),则其访问权限为default(默认访问权限)。默
认访问权限的类、类属变量及方法,包内的任何类(包括继承了此类的子类)都可以访问它,而对于包外的任何类都不能访问它(包括包外继承了此类的子类)。default重点突出包;
(4)private: 用private修饰的类、类属变量及方法,只有本类可以访问,而包内包外的任何类均不能访问它。
网上一些资料及一些书上用表格对java访问修饰符做了清楚的总结,如下表所示:
访问级别 |
访问控制修饰符 |
同类 |
同包 |
子类 |
不同的包 |
公开 |
public |
√ |
√ |
√ |
√ |
受保护 |
protected |
√ |
√ |
√ |
-- |
默认 |
没有访问控制修饰符 |
√ |
√ |
-- |
-- |
私有 |
private |
√ |
-- |
-- |
-- |
本人以为该表有些问题交代不清楚,如访问修饰符protected中,不同的包不可以访问,而子类可以访问,那试问位于不同包中的子类是能访问还是不能访问呢?因此本人在自
己理解的基础上,为了自己理解方便,容易记忆,重新整理了一个表格如下:
访问级别 |
访问控制修饰符 |
同类 |
同包不同类(不含子类) |
同包子类 |
不同包不同类 (不含子类) |
不同包子类 |
公开 |
public |
√ |
√ |
√ |
√ |
√ |
受保护 |
protected |
√ |
√ |
√ |
-- |
√(注意) |
默认 |
没有访问控制修饰符 |
√ |
√ |
√ |
-- |
-- |
私有 |
private |
√ |
--- |
--- |
-- |
-- |
重要总结:通过上面的分析,我们可以看到:
1. public、private和protected对我们来说没有任何异议。
2. 顶层类只能用public访问修饰符和default(默认)访问修饰符修饰,其中用默认修饰符修饰的类(及没有任何修饰符的类,如class B{})不能被其他包中的类继承,这也说明了default(默认)访问修饰符突出的是包权限
3. protected:本人做了一次实验,发现在不同包的子类中,new一个父类对象,并用该父类对象去访问父类中的用protected修饰的类属变量和方法时不能访问,而new一个子类对象时,子类对象可以访问(说明protected修饰的类可以被其他包中的类继承)。也可以在子类重写父类的方法中使用super关键字调用。这岂不是和上面表格中的总结(红色对勾)冲突了?本人也是百思不得其解。最后在网上找到了一个相对比较认可的解释,如下:
4. 如果一个类使用public修饰,那该类的类名必须与他所在的源文件名相同。一个.java源文件中有且只有一个public类,顶层类只能用public和默认修饰符(即无修饰符)修饰;
5. final修饰的类不能被继承,没有子类。
6. abstract修饰的类不能被实例化,必须被子类继承。类只要有一个抽象方法就必定是抽象类,但抽象类不一定要有抽象方法。
最终总结:protected修饰符所修饰的类(这句话中指父类)属成员变量和方法,只可以被子类访问,而不管子类是不是和父类位于同一个包中。default修饰符所修饰的类属成员变量和方法,只可被同一个包中的其他类访问,而不管其他类是不是该类的子类。protected属于子类限制修饰符,而default属于包限制修饰符。
四.练习总结
1.final修饰类,修饰方法,修饰变量有什么特点?
(1)final修饰类,这个类不能被继承,但还是个类,可以继承其他类
(2)在测试类中实例化对象,用对象调用方法都可以
(3)当final修饰父类中的方法时,子类不可以重写此方法
(4)final修饰变量就变成了常量,只能被赋值一次,终身不变
(5)final修饰引用类型的变脸时,只能被赋值一个地址一次,终身不变
(6)final修饰成员变量时,只能在创建对象以前给他赋值
(7)可以直接在类里面赋值,也可以用构造方法给他赋值,但是不能用set方法给赋值
2、package,import,class之间有没有顺序关系?
package>import>class
3、Java中的权限修饰符有哪些?
public 、protected 、private 、default
4、内部类的访问特点是什么?
(1)内部类可以直接访问外部类中的成员。
(2)外部类要访问内部类,必须建立内部类的对象。
5、局部内部类访问局部变量,该局部变量必须用哪个关键字修饰,为什么?
局部内部类访问局部变量,必须使用final关键字
对于普通局部变量他的作用域就是该方法内,当方法结束该局部变量就随之消失;
但局部内部类可能产生隐式的“闭包”,闭包将使得局部变量脱离他所在的方法继续存在。
由于内部类可能扩大局部变量的作用域,如果再加上这个被内部类访问的局部变量没有使用final修饰,
也就是说这个变量的值可以随时改变,此变量已经消失不能修改了,
因此java编译器要求所有被内部类访问的局部变量必须使用final修饰符修饰。
6、内部类的格式? 它的本质是什么?
class 外部类 {
修饰符 class 内部类 {
//其他代码
}
}
7、匿名内部类的前提条件,格式分别是什么?
前提条件:必须是继承或者接口
格式:
new 接口或者父类(){
重写抽象方法;
};
8.代码块的分类?
代码块分为静态代码块和构造代码块