day15_包装类丶内部类

Source

包装类

Java提供了两个类型系统,基本类型与引用类型,使用基本类型在于效率,然而当要使用只针对对象设计的API或新特性(例如泛型),那么基本数据类型的数据就需要用包装类来包装。

装箱与拆箱

  • 装箱:把基本数据类型转为包装类对象。转为包装类的对象,是为了使用专门为对象设计的API和特性
Integer i1 = new Integer(4);//使用构造函数函数
Integer i2 = Integer.valueOf(4);//使用包装类中的valueOf方法
  • 拆箱:把包装类对象拆为基本数据类型。转为基本数据类型,一般是因为需要运算,Java中的大多数运算符是为基本数据类型设计的。比较、算术等
Integer i1 = new Integer(4);
int num1 = i1.intValue();

JDK1.5之后,可以自动装箱与拆箱。注意:只能与自己对应的类型之间才能实现自动装箱与拆箱。

Integer i = 4;//自动装箱。相当于Integer i = Integer.valueOf(4);
i = i + 5;//等号右边:将i对象转成基本数值(自动拆箱) i.intValue() + 5;
//加法运算完成后,再次装箱,把基本数值转成对象。

总结:对象(引用数据类型)能用的运算符有哪些?

  • instanceof
  • =:赋值运算符
  • ==和!=:用于比较地址,但是要求左右两边对象的类型一致或者是有父子类继承关系。
  • 对于字符串这一种特殊的对象,支持“+”,表示拼接。

包装类不能自动类型提升

byte  b = 1;
Integer = b; //错误

包装类的一些API

把基本数据类型转为字符串

int a = 10;
//String str = a;//错误的
//方式一:
String str = a + "";
//方式二:
String str = String.valueOf(a);

把字符串转为基本数据类型

String转换成对应的基本类型 ,除了Character类之外,其他所有包装类都具有parseXxx静态方法可以将字符串参数转换为对应的基本类型,例如:

  • public static int parseInt(String s):将字符串参数转换为对应的int基本类型。

或把字符串转为包装类,然后可以自动拆箱为基本数据类型

  • public static Integer valueOf(String s):将字符串参数转换为对应的Integer包装类,然后可以自动拆箱为int基本类型

注意:如果字符串参数的内容无法正确转换为对应的基本类型,则会抛出java.lang.NumberFormatException异常。

int a = Integer.parseInt("整数的字符串");
double d = Double.parseDouble("小数的字符串");
boolean b = Boolean.parseBoolean("true或false");

int a = Integer.valueOf("整数的字符串");
double d = Double.valueOf("小数的字符串");
boolean b = Boolean.valueOf("true或false");

数据类型的最大最小值

Integer.MAX_VALUE和Integer.MIN_VALUE
Long.MAX_VALUE和Long.MIN_VALUE
Double.MAX_VALUE和Double.MIN_VALUE

字符转大小写

Character.toUpperCase('x');
Character.toLowerCase('X');

整数转进制

Integer.toBinaryString(int i) 
Integer.toHexString(int i)
Integer.toOctalString(int i)

包装类对象的缓存问题

 包装类有一个缓冲区,没有超过缓冲区会去new一个新对象,没有超过缓冲区则共享对象。

Integer i = 1;
Integer j = 1;
System.out.println(i == j);//true

Integer i = 128;
Integer j = 128;
System.out.println(i == j);//false

Integer i = new Integer(1);//新new的在堆中
Integer j = 1;//这个用的是缓冲的常量对象,在方法区
System.out.println(i == j);//false

Integer i = new Integer(1);//新new的在堆中
Integer j = new Integer(1);//另一个新new的在堆中
System.out.println(i == j);//false

Double d1 = 1.0;
Double d2 = 1.0;
System.out.println(d1==d2);//false 比较地址,没有缓存对象,每一个都是新new的

这类Integer等包装类对象和String是“不可变”对象,即一旦修改,就是新对象,和实参就无关了。

Junit单元测试

大神所写,膜拜一下:https://blog.csdn.net/weixin_44170221/article/details/106463482

内部类

什么是内部类?

  • 将一个类A定义在另一个类B里面,里面的那个类A就称为内部类,B则称为外部类

为什么要声明内部类呢?

  • 当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,不在其他地方单独使用,那么整个内部的完整结构最好使用内部类。
  • 而且内部类因为在外部类的里面,因此可以直接访问外部类的私有成员。

内部类都有哪些形式

根据内部类声明的位置(如同变量的分类),我们可以分为:

  • 成员内部类:
    • 静态成员内部类
    • 非静态成员内部类
  • 局部内部类
    • 有名字的局部内部类
    • 匿名的内部类

静态内部类

静态内部类的特点:

  • 和其他类一样,它只是定义在外部类中的另一个完整的类结构
    • 可以继承自己的想要继承的父类,实现自己想要实现的父接口们,和外部类的父类和父接口无关
    • 可以在静态内部类中声明属性、方法、构造器等结构,包括静态成员
    • 可以使用abstract修饰,因此它也可以被其他类继承
    • 可以使用final修饰,表示不能被继承
    • 编译后有自己的独立的字节码文件,只不过在内部类名前面冠以外部类名和$符号。
  • 和外部类不同的是,它可以允许四种权限修饰符:public,protected,缺省,private
    • 外部类只允许public或缺省的
  • 可以在静态内部类中使用外部类的静态成员
    • 在静态内部类中不能使用外部类的非静态成员哦
  • 在外部类的外面不需要通过外部类的对象就可以创建静态内部类的对象
  • 如果在内部类中有变量与外部类的静态成员变量同名,可以使用“外部类名."进行区别
class Outer  {

    int num = 10;
    private static int age = 66;

    static int kk = getNum();

    private static int getNum() {
        System.out.println("66666666");
        return 20;
    }


    public void outerMethod1() {
        System.out.println("this is outerMethod1");
    }

    public static void outerMethod2() {
        System.out.println("this is static outerMethod2");
       // System.out.println(Inner.name);
    }

     static class Inner {

        int num = 66;
        static int age = getNum();

        static String name = "李白";

        public void innerMethod() {

            // System.out.println(num);

            System.out.println(age);
            outerMethod2();
        }

        public static void innerMethod2(){

            int age = 199;
            System.out.println("局部变量:"+age);
            System.out.println("inner:"+Inner.age);
            System.out.println("outer:"+Outer.age);

            System.out.println("this is static innerMethod2");
        }

    }
}

class Animal {
}

class Person {
}

定义测试类

import org.junit.Test;

import java.io.OutputStream;

/*
内部类:
   把一个类定义另一个类的内部

外部类:内部类外边的类 叫外部类
  外部类只能被 public 与 default修饰

静态内部类:
一.语法结构:

class 外部类名{

 //内部类定义
 [权限修饰符4种] static [final] class 类名{


 }
}
二.作用
  1.可以打破java单继承的限制

  2.在内部类中做更多的功能 为外部类服务

  3.可以资源的隐藏

三.特点:

1.静态内部类可以访问外部类的资源
     静态的属性
     静态的方法

2.外部类能否使用内部类的资源
   如果是静态资源 可以直接通过 内部类名.资源名
   如果是非静态资源  那么 需要通过 内部类的对象.资源名

3.当类中有内部类的时候
   外部类名$内部类名
 */
public class TestStaicClass {

    public static void main(String[] args) {


        Outer.outerMethod2();
       /* Outer o = new Outer();
        o.outerMethod1();*/
       /*//创建内部类的对象
        Outer.Inner inner = new Outer.Inner();
        inner.innerMethod();
        Outer.Inner.innerMethod2();*/
    }
}

非静态成员内部类

非静态内部类的特点:

  • 和其他类一样,它只是定义在外部类中的另一个完整的类结构
    • 可以继承自己的想要继承的父类,实现自己想要实现的父接口们,和外部类的父类和父接口无关
    • 可以在非静态内部类中声明属性、方法、构造器等结构,但是不允许声明静态成员,但是可以继承父类的静态成员,而且可以声明静态常量
    • 可以使用abstract修饰,因此它也可以被其他类继承
    • 可以使用final修饰,表示不能被继承
    • 编译后有自己的独立的字节码文件,只不过在内部类名前面冠以外部类名和$符号。
  • 和外部类不同的是,它可以允许四种权限修饰符:public,protected,缺省,private
    • 外部类只允许public或缺省的
  • 还可以在非静态内部类中使用外部类的所有成员,哪怕是私有的
  • 在外部类的静态成员中不可以使用非静态内部类哦
    • 就如同静态方法中不能访问本类的非静态成员变量和非静态方法一样
  • 在外部类的外面必须通过外部类的对象才能创建非静态内部类的对象
    • 因此在非静态内部类的方法中有两个this对象,一个是外部类的this对象,一个是内部类的this对象

代码实例

public class Outer {

    static int age = 20;
    int num = 30;

    class Inner {

        final static int ll = 90;
        String name = "李白";

        int num  =50;

        public void innerMethod2() {
            int num = 66;
            System.out.println(num);
            System.out.println(this.num);
            System.out.println(Outer.this.num);

        }

        public void innerMethod() {
            System.out.println(age);
            System.out.println(num);
           /* outerMethod1();
            outerMethod2();*/
        }

    }

    class  Inner1{}

    public void outerMethod1() {

        Inner in = new Inner();

        System.out.println(Inner.ll);

        System.out.println(in.name);

        in.innerMethod();
        System.out.println("this is outerMethod1()");
    }

    public static void outerMethod2() {
        System.out.println("this is static outerMethod2()");
    }


}

定义测试类

package com.atguigu.innerclass.nostaticclass;
/*
非静态内部类:
  不加static修饰的 内部类

  外部类 类名{

  [权限修饰符] class  内部类名{

  }
  }
特点:

   1.非静态内部类可以直接使用外部类所有的资源
        静态与非静态资源
        私有与非私有资源

   2.外部类使用内部类的资源
      1.首先创建非静态内部类的对象 然后才能使用内部类中的资源
      2.如果是内部类中的静态常量 则可以直接使用
      3.外部类的静态方法 不能使用内部类资源

   3.非静态内部类中不能存在静态的方法静态的属性
     但是可以存在静态的常量


   4.有几个非静态内部类 就会生成几个字节码文件
     外部类名$内部类名

   5.每一个类中都有一个this 代指当前对象
     获取外部类中的this  外部类名.this.属性名



 */
public class Test {
    public static void main(String[] args) {
       //  Outer outer = new Outer();
       // Outer.Inner inner = outer.new Inner();
       //  System.out.println(outer.num);
        //使用非静态内部类对象
        Outer.Inner inner = new Outer ().new Inner();
        inner.innerMethod2();
    }
}

局部内部类

语法格式:

局部内部类的特点:

  • 和外部类一样,它只是定义在外部类的某个方法中的另一个完整的类结构
    • 可以继承自己的想要继承的父类,实现自己想要实现的父接口们,和外部类的父类和父接口无关
    • 可以在局部内部类中声明属性、方法、构造器等结构,但不包括静态成员,除非是从父类继承的静态常量
    • 可以使用abstract修饰,因此它也可以被同一个方法的在它后面的其他内部类继承
    • 可以使用final修饰,表示不能被继承
    • 编译后有自己的独立的字节码文件,只不过在内部类名前面冠以外部类名、$符号、编号。
      • 这里有编号是因为同一个外部类中,不同的方法中存在相同名称的局部内部类
  • 和成员内部类不同的是,它前面不能有权限修饰符等
  • 局部内部类如同局部变量一样,有作用域
  • 局部内部类中是否能访问外部类的静态还是非静态的成员,取决于所在的方法是否是静态/非静态
  • 局部内部类中还可以使用所在方法的局部常量,即用final声明的局部变量
    • JDK1.8之后,如果某个局部变量在局部内部类中被使用了,自动加final

示例代码:

interface Run {
    void run();
}

public class Student {

    public static void main(String[] args) {

        Student s = new Student();
        Run run = s.outerMethod();

        run.run();

    }

    public Run outerMethod() {

        int num = 20;

        class Inner implements Run {
            @Override
            public void run() {
                System.out.println("this is innerMethod\t" + num);
            }
        }
        return new Inner();
    }
}

定义测试类

/*
局部内部类:局部变量【特别不重要】

语法结构:
 外部类 类名{
    [权限修饰符] [static] 返回值类型 方法名(形参列表){
        [修饰符] class 类名{


        }
    }
 }

 注意:
    1.局部内部类 只能被 default修饰
    2.局部内部类 可以被 final /abstract修饰

    3.局部内部类使用外部类的资源要看外部类的方法是静态还是非静态
       方法为非静态  可以使用外部类静态或者非静态的资源
       方法为静态  可以直接使用外部类中静态的资源
    4.局部内部类编译成功后 也会产生对应的字节码文件
       外部类的名字$序号内部类的名字
       Person$2Inner.class

    5.局部内部类中 不能存在静态的属性但是可以存在静态的常量

    6.在方法内 创建内部类对象  通过内部类对象 调用 内部类中的方法

    7.当在局部内部类的方法内 使用 局部变量时  那么变量前会自动加一个final(jdk1.8)

 */
public class Person {

    int age = 30;

    static String name = "李白";

    public static void main(String[] args) {

        Person p = new Person();

        p.show();

       // Person.outerMethod();
    }


 public void show() {
        //局部变量
        int num = 20;
        class Inner {
            int age = 90;
            final static int num = 99;
            public void innnerMethod() {
               /* System.out.println(age);
                System.out.println(name);*/

                System.out.println("this is innnerMethod()");
            }
        }
      /*  Inner in = new Inner();

        in.innnerMethod();*/
    }

}

匿名内部类

我们在开发过程中,需要用到一个抽象类的子类的对象或一个接口的实现类的对象,而且只创建一个对象,而且逻辑代码也不复杂。那么我们原先怎么做的呢?

  1. 编写类,继承这个父类或实现这个接口
  2. 重写父类或父接口的方法
  3. 创建这个子类或实现类的对象

这里,因为考虑到这个子类或实现类是一次性的,那么我们“费尽心机”的给它取名字,就显得多余。那么我们完全可以使用匿名内部类的方式来实现,避免给类命名的问题。

语法格式

注意:

匿名内部类是一种特殊的局部内部类,只不过没有名称而已。所有局部内部类的限制都适用于匿名内部类。例如:

  • 在匿名内部类中是否可以使用外部类的非静态成员变量,看所在方法是否静态
  • 在匿名内部类中如果需要访问当前方法的局部变量,该局部变量需要加final

匿名内部类是创建的子类对象,这对象能做什么呢?

  • 使用方式一:匿名内部类的对象直接调用方法​​​​​​​​​​​​​​

定义接口使用匿名内部类的对象直接调用方法

package demo01;
// 父接口
interface A {
    void a();
}

public class Test {
    public static void main(String[] args) {
        //匿名内部类
        new A() {
            @Override
            public void a() {
                System.out.println("aaaa");
            }
        }.a();
    }
}

定义类使用匿名内部类的对象直接调用方法

// 父类
class B{
    public void b(){
        System.out.println("bbbb");
    }
}

public class Test {
    public static void main(String[] args) {
        //匿名内部类
        new B() {
            @Override
            public void b() {
                System.out.println("aaaa");
            }
        }.b();
    }
}
  • 使用方式二:通过父类或父接口的变量多态引用匿名内部类的对象
interface A {
    void a();
}

public class Test {
    public static void main(String[] args) {
        //通过父类或父接口的变量多态引用匿名内部类的对象
        A obj = new A() {
            @Override
            public void a() {
                System.out.println("aaaa");
            }
        };
        obj.a();
    }
}
  • 使用方式三:匿名内部类的对象作为实参
interface A{
    void method();
}
public class Test{
    //匿名内部类的对象作为实参
    public static void test(A a){
        a.method();
    }

    public static void main(String[] args){
        test(new A(){
            @Override
            public void method() {
                System.out.println("aaaa");
            }

        });
    }
}