内部类
概念: 定义在类中的类
内部类可以是静态static的,也可用public,default,protected和private修饰。
划分: 成员内部类、局部内部类、匿名内部类
为什么要用内部类: 类的生命周期程序运行时开始到程序结束时销毁,但是呢,某些情况下,在整个应用程序中对该类的使用频率较低,那此时就需要将其定义为内部类,以缩短其生命周期。
使用场景: 当类或该类的对象并不是很常用的时候,就可以定义为内部类
成员内部类
成员内部类,就是作为外部类的成员,可以直接使用外部类的所有成员和方法,即使是private的。同时外部类要访问内部类的所有成员变量/方法,则需要通过内部类的对象来获取。
要注意的是,成员内部类不能含有static的变量和方法。因为成员内部类需要先创建了外部类,才能创建它自己的,了解这一点,就可以明白更多事情,在此省略更多的细节了。
在成员内部类要引用外部类对象时,使用outer.this来表示外部类对象;
而需要创建内部类对象,可以使用outer.inner obj = outerobj.new inner();
class Outer{
private int id = 10;
public void out(){
System.out.println("这是外部类的方法");
}
// 注: 这个public不能省略
// 注: 内部类可以加上static 修饰, 就叫静态内部类, 缺点是无法直接调用非静态的属性值;
public class Inner{
public void in(){
System.out.println("这是内部类的方法");
}
// 获得外部类的私有属性~
public void getID(){
System.out.println(id);
}
}
}
public class Application{
public static void main(String[] args){
Outer outer = new Outer();
// 通过这个外部类来实例化内部类~
outer.Inner() inner = outer.new Inner();
inner.in();
inner.getID();
}
}
局部内部类
局部内部类,是指内部类定义在方法和作用域内。
class Outer{
// 局部内部类
public void method(){
class Inner{
public void in(){
}
}
}
}
public class Application {
public static void main(String[] args){
Outer outer = new Outer();
}
}
局部内部类也像别的类一样进行编译,但只是作用域不同而已,只在该方法或条件的作用域内才能使用,退出这些作用域后无法引用的。
匿名内部类
没有类名的局部内部类(一切特征都与局部内部类相同)
必须继承一个父类或者实现一个接口
定义类、实现类、创建对象的语法合并,只能创建一个该类的对象(类信息只用到一次)
减少代码量,书写思路流程。
public class TestInnerLocalForApply {
public static void main(String[] args) {
//学校开设新班
/*1.
Teacher teacher = new AdvancedTeacher();//家长提出意见需要高级教师
teacher.teach();//问题就是校方没办法提供如此多的高级教师
*/
//校方出台了内部规则(按照班级的奇偶编号进行均匀分派,奇数初级,偶数高级)
Teacher t =School.getTeacher(1);
t.teach();
Teacher t1 =School.getTeacher(2);
t1.teach();
}
}
class School{
public static Teacher getTeacher(int classNo) {//使用静态方法,无需new School
Teacher currentTeacher = null;
if(classNo%2 !=0) {
currentTeacher = new Teacher() {
@Override
public void teach() {
System.out.println("初级老师在上课");
}
};
}else {
currentTeacher = new Teacher() {
@Override
public void teach() {
System.out.println("高级老师在上课");
}
};
}
return currentTeacher;
}
}
abstract class Teacher{
public abstract void teach();
}
自动拆装箱
自动装箱和拆箱,就是基本类型和引用类型的相互转换。
包装类型
Java语言是一个面向对象的语言,但是Java中的基本数据类型却是不面向对象的,这在实际使用时存在很多的不便,为了解决这个不足,在设计类时为每个基本数据类型设计了一个对应的类进行代表,这样八个和基本数据类型对应的类统称为包装类(Wrapper Class)。
包装类均位于java.lang包,包装类和基本数据类型的对应关系如下表所示
基本数据类型 | 包装类 |
---|---|
short | Short |
char | Character |
int | Integer |
long | Long |
float | Float |
double | Double |
boolean | Boolean |
在这八个类名中,除了Integer和Character类以后,其它六个类的类名和基本数据类型一致,只是类名的第一个字母大写即可。
为什么需要包装类
很多人会有疑问,既然Java中为了提高效率,提供了八种基本数据类型,为什么还要提供包装类呢?
这个问题,其实前面已经有了答案,因为Java是一种面向对象语言,很多地方都需要使用对象而不是基本数据类型。比如,在集合类中,我们是无法将int 、double等类型放进去的。因为集合的容器要求元素是Object类型。
为了让基本类型也具有对象的特征,就出现了包装类型,它相当于将基本类型“包装起来”,使得它具有了对象的性质,并且为其添加了属性和方法,丰富了基本类型的操作。
自动拆箱与自动装箱
在Java SE5中,为了减少开发人员的工作,Java提供了自动拆箱与自动装箱功能。
自动装箱: 就是将基本数据类型自动转换成对应的包装类。
自动拆箱:就是将包装类自动转换成对应的基本数据类型。
//手动装箱
int i=1;
Integer a=new Integer(i);
//自动装箱
Integer b=1;
Integer j=new Integer(8);
//手动拆箱
int m=j.intValue();
//自动拆箱
int n=j;
一些例子
public static void main(String[] args) {
Integer a=new Integer(123);
Integer b=new Integer(123);
System.out.println(a==b);//输出 false
Integer c=123;
Integer d=123;
System.out.println(c==d);//输出 true
Integer e=129;
Integer f=129;
System.out.println(e==f);//输出 false
int g=59;
Integer h=new Integer(59);
System.out.println(g==h);//输出 true
}
代码1好理解,因为新建了不同的对象。
代码2为什么返回true呢?原来自动装箱的源码简化如下:
public static Integer valueOf(int i) {
if (i >= -128 && i <= 127)
return IntegerCache.cache[i + 127];
//如果i的值大于-128小于127则返回一个缓冲区中的一个Integer对象
return new Integer(i);
//否则返回 new 一个Integer 对象
}
IntegerCache 类在初始化的时候,生成了一个大小为 256 的integer 类型的常量池,并且integer.val 的值从-128-127,当我们运行 Integer c=a ;时,如果 -128<=a<=127时,不会再生成新的integer对象,直接从常量池中找到对应的已经初始化后的对象。当 a<-128||a>127时才会生成一个新的对象。所以代码3返回的也是false
值得注意的是代码4中,最后在比较g h 的值时,用了自动拆箱,g == h 这一代码执行的是:g==h.IntValue(),而h.IntValue()= 59 。所以实际上比较的是两个int类型,返回为true。