JAVA面向对象(下)(四、内部类、枚举、包装类)

一、内部类(续)

1.1 内部类的分类

按照声明的位置划分,可以把内部类分为两大类:

  • 成员内部类:方法外

  • 局部内部类:方法内

public class 外部类名{
    【修饰符】 class 成员内部类名{ //方法外
        
    }
    
    【修饰符】 返回值类型 方法名(【形参列表】){//方法内
       【修饰符】 class 局部内部类名{
        
        }
    }
}

 

成员内部类根据是不是有static修饰,又可以分为:

  • 静态成员内部类,简称静态内部类

  • 非静态成员内部类

局部内部类根据是不是有名字,可以分为:

  • 有名字的局部内部类,简称局部内部类

  • 匿名的局部内部类,简称匿名内部类

静态内部类非静态成员内部类局部内部类匿名内部类
声明的位置方法外方法外方法内方法内
是否有名字
是否有static
是否有权限修饰符(public,protected,private)可以可以
是否是一次性不是不是不是是,必须声明类的同时new对象

1.2 局部内部类(用的最少,几乎不用)

需求:

  • 声明一个接口Flyable,包含抽象方法 void fly()

  • 在测试类的主方法中,用局部内部类实现Flyable接口

局部内部类有名字,也有自己的独立的字节码文件,它的文件名:外部类名$数字编号局部内部类名.class。这里有编号的原因是因为同一个外部类的不同方法中,可能存在同名的局部内部类。

局部内部类,可以直接使用外部类的私有成员。

局部内部类,可以直接使用外部类的局部变量。但是这个局部变量是final的。只要局部变量被局部内部类使用了,那么JDK8之前,必须手动加final,JDK8开始,默认自动加final。

问:为什么它必须加final声明?

示例代码1

Flyable接口
package com.atguigu.inner.local;

public interface Flyable {
    void fly();//抽象方法,省略了public abstract
}

 

测试类1
package com.atguigu.inner.local;

public class TestFlyable {
    public static void main(String[] args) {
        //匿名的局部内部类
        Flyable f1 = new Flyable() {
            @Override
            public void fly() {
                System.out.println("我要飞的更高!");
            }
        };
        Flyable f2 = new Flyable() {
            @Override
            public void fly() {
                System.out.println("我要飞的更高!");
            }
        };
        //上面的代码,既是两个类,也是两个对象
        //每一个类有一个对象
        //因为匿名内部类是一次性,如果需要创建多个对象,
        //类就会声明多次

        f1.fly();
        f2.fly();
    }
}
测试类2
package com.atguigu.inner.local;

public class TestFlyable2 {
    public static void main(String[] args) {
        //有名字的局部内部类
        class Bird implements Flyable{
            @Override
            public void fly() {
                System.out.println("我要飞的更高!");
            }
        }
        Flyable f1 = new Bird();
        Flyable f2 = new Bird();

        f1.fly();
        f2.fly();
    }

    public static void other(){
        //有名字的局部内部类
        class Bird implements Flyable{
            @Override
            public void fly() {
                System.out.println("我要飞的更高2!");
            }
        }
    }
}

示例代码2

package com.atguigu.inner.local;

public class TestOuterInner {
    //成员变量
    private static int a;//静态变量
    private  int b;//实例变量

    //m1方法是静态的,不允许访问非静态的b
    public static void m1(){
        final int c = 1;//局部变量
        class InnerOne{
            public void test1(){
                System.out.println("a = " + a);
//                System.out.println("b = " + b);
                System.out.println("c = " + c);
            }
        }
    }

    public void m2(){
        final int c = 1;//局部变量
//        c = 2;//不允许再赋值
        class InnerTwo{
            public void test2(){
                System.out.println("a = " + a);
                System.out.println("b = " + b);
                System.out.println("c = " + c);
            }
        }
    }

    public static Flyable getABird(){
        int height = 1000;
        class Bird implements Flyable{
            @Override
            public void fly() {
                System.out.println("我要飞的更高,高度达到:"  +height);
            }
        }

        Bird b = new Bird();
        return b;
    }

    public static void main(String[] args) {
        Flyable b = getABird();
        b.fly();
    }
}

1.3 静态内部类

案例需求

  • 声明一些容器类,这些容器类的对象是用来装一组元素对象的。

  • 声明一个动态数组的容器类

    • 声明一个属性Object[] all数组,用于存储元素

    • 声明一个属性int total,用于记录数组中实际存储了几个元素

    • 声明一个公共的add方法,用于添加元素到数组中

    • 重写toString方法,用于拼接数组中的所有元素

  • 声明一个单链表的容器类

    • 声明一个私有的静态内部类 SingleNode,用于描述单链表的结点的特征

      • 包含属性Object element,用于存储元素本身

      • 包含属性SingleNode next,用于记录下一个结点的首地址

      • 声明有参构造,用于创建单链表结点

    • 声明一个公共的add方法,用于添加元素到单链表中

    • 声明一个私有的findLast方法,用于查找单链表的最后一个结点

    • 重写toString方法,用于拼接单链表中的所有元素

示例代码1

MyArrayList动态数组类
package com.atguigu.inner.staticinner;

public class MyArrayList {//动态数组容器类
    private Object[] all = new Object[4];
    private int total;//默认值是0

    public void add(Object obj) {
        if (total >= all.length) {//数组已满,需要扩容
            //创建一个新数组,长度是原来数组的2倍
            Object[] newArr = new Object[all.length * 2];
            //复制total个元素到新数组中
            for (int i = 0; i < total; i++) {
                newArr[i] = all[i];
            }
            //all指向新数组,扔掉旧数组
            all = newArr;
        }
        //把新元素放到[total]位置,total自增1,代表元素个数增加,同时也代表下一个元素的位置后移
        all[total++] = obj;
    }

    @Override
    public String toString() {
        String str = "[";
        for (int i = 0; i < total; i++) {
            str += all[i] + (i == total - 1 ? "]" : ",");
        }
        return str;
    }
}
测试类
package com.atguigu.inner.staticinner;

public class TestMyArrayList {
    public static void main(String[] args) {
        //创建容器对象,比喻准备了一个教室
        MyArrayList list = new MyArrayList();
        //学生陆续进入教室
        list.add("张三");
        list.add("李四");
        list.add("王五");
        list.add("赵六");
        list.add("方总");

        System.out.println(list);
    }
}

示例代码2

SingleLink单链表类
package com.atguigu.inner.staticinner;

public class SingleLink {//单链表
    private SingleNode first;//单链表的第一个结点

    //静态内部类,这个内部类在别的地方没用,仅限于当前外部类使用,所以将它声明为private
    private static class SingleNode{
        //属性有俩:(1)元素(2)下一个结点
        Object element;
        SingleNode next;

        SingleNode(Object element, SingleNode next) {
            this.element = element;
            this.next = next;
        }
    }

    public void add(Object obj){
        //(1)创建结点对象,用来包裹我们的元素对象obj
        SingleNode newNode = new SingleNode(obj, null);
        //这里null代表新结点没有下一个结点

//        (2)分情况讨论
        if(first == null){//链表是空的
            first = newNode;//当前结点就是链表的第一个结点
        }else{
            //找到最后一个
            SingleNode last = findLast();
            //让把新结点连接到最后一个结点的后面
            last.next = newNode;
        }
    }

    private SingleNode findLast(){
        SingleNode node = first;
        while(node.next != null){
            node = node.next;
        }
        return node;
    }

    @Override
    public String toString() {
        String str = "[";
        SingleNode node = first;
        while(node.next != null){
            str += node.element +",";//拼接结点的元素
            node = node.next;
        }
        //出了循环node是代表最后一个结点,它的元素没有拼接到str中
        str += node.element +"]";
        return str;
    }
}

测试单链表类
package com.atguigu.inner.staticinner;

public class TestSingleLink {
    public static void main(String[] args) {
        SingleLink link = new SingleLink();
        link.add("张三");
        link.add("李四");
        link.add("王五");
        link.add("赵六");

        System.out.println(link);
    }
}

2.4 非静态内部类

  • 声明一个动态数组的容器类MyArrayList(续)(见上面2.3)

    • 再声明一个public的非静态内部类迭代器Itr,用于描述迭代器的特征

      • 包含属性:int index,表示迭代器指向的元素的下标

      • 包含方法:public boolean hasElement():判断迭代器当前位置是不是有元素

      • 包含方法:public Object getElementAndMoveNext():返回迭代器当前位置的元素,然后迭代器往后移动

  • 声明一个单链表的容器类SingleLink(续)(见上面2.3)

    • 再声明一个public的非静态内部类迭代器Itr,用于描述迭代器的特征

      • 包含属性:SingleNode node =first,表示迭代器默认指向单链表的第一个结点

      • 包含方法:public boolean hasElement():判断迭代器当前位置是不是有元素

      • 包含方法:public Object getElementAndMoveNext():返回迭代器当前位置的元素,然后迭代器往后移动

示例代码1

MyArrayList动态数组类(升级)
package com.atguigu.inner.nonstatic;

public class MyArrayList {//动态数组容器类
    private Object[] all = new Object[4];
    private int total;//默认值是0

    public void add(Object obj) {
        if (total >= all.length) {//数组已满,需要扩容
            //创建一个新数组,长度是原来数组的2倍
            Object[] newArr = new Object[all.length * 2];
            //复制total个元素到新数组中
            for (int i = 0; i < total; i++) {
                newArr[i] = all[i];
            }
            //all指向新数组,扔掉旧数组
            all = newArr;
        }
        //把新元素放到[total]位置,total自增1,代表元素个数增加,同时也代表下一个元素的位置后移
        all[total++] = obj;
    }

    @Override
    public String toString() {
        String str = "[";
        for (int i = 0; i < total; i++) {
            str += all[i] + (i == total - 1 ? "]" : ",");
        }
        return str;
    }

    public class Itr{//非静态的成员内部类
        int index;//默认值是0

        public boolean hasElement(){
            return index < total;
        }

        public Object getElementAndMoveNext(){
            return all[index++];
        }
    }
}

 

测试类
package com.atguigu.inner.nonstatic;

public class TestMyArrayListItr {
    public static void main(String[] args) {
        MyArrayList list = new MyArrayList();
        list.add("张三");
        list.add("李四");
        list.add("王五");

        /*
        (1)MyArrayList.Itr代表类型,因为Itr是MyArrayList的成员内部类,
        所以Itr的全名称是MyArrayList.Itr
        (2)=右边为什么是list.new Itr();
        Itr作为MyArrayList的非静态成员,访问一个类的非静态成员,
        就需要用这个类的对象。
         */
        MyArrayList.Itr iterator = list.new Itr();
        while(iterator.hasElement()){
            Object element = iterator.getElementAndMoveNext();
            System.out.println(element);
        }
    }
}

 

示例代码2

SingleLink单链表类(升级)
package com.atguigu.inner.nonstatic;

public class SingleLink {//单链表
    private SingleNode first;//单链表的第一个结点

    //静态内部类,这个内部类在别的地方没用,仅限于当前外部类使用,所以将它声明为private
    private static class SingleNode{//结点
        //属性有俩:(1)元素(2)下一个结点
        Object element;
        SingleNode next;

        SingleNode(Object element, SingleNode next) {
            this.element = element;
            this.next = next;
        }
    }

    public void add(Object obj){
        //(1)创建结点对象,用来包裹我们的元素对象obj
        SingleNode newNode = new SingleNode(obj, null);
        //这里null代表新结点没有下一个结点

//        (2)分情况讨论
        if(first == null){//链表是空的
            first = newNode;//当前结点就是链表的第一个结点
        }else{
            //找到最后一个
            SingleNode last = findLast();
            //让把新结点连接到最后一个结点的后面
            last.next = newNode;
        }
    }

    private SingleNode findLast(){
        SingleNode node = first;
        while(node.next != null){
            node = node.next;
        }
        return node;
    }

    @Override
    public String toString() {
        String str = "[";
        SingleNode node = first;
        while(node.next != null){
            str += node.element +",";//拼接结点的元素
            node = node.next;
        }
        //出了循环node是代表最后一个结点,它的元素没有拼接到str中
        str += node.element +"]";
        return str;
    }

    public class Itr{
        SingleNode node = first;//默认指向单链表的第一个结点

        public boolean hasElement(){
            return node != null;
        }

        public Object getElementAndMoveNext(){
            Object element = node.element;
            node = node.next;
            return element;
        }
    }
}
测试类
package com.atguigu.inner.nonstatic;


public class TestSingleLinkItr {
    public static void main(String[] args) {
        SingleLink link = new SingleLink();
        link.add("张三");
        link.add("李四");
        link.add("王五");
        link.add("赵六");

        SingleLink.Itr iterator = link.new Itr();
        while(iterator.hasElement()){
            Object element = iterator.getElementAndMoveNext();
            System.out.println(element);
        }
    }
}

2.5 静态内部类和非静态内部类的选择

原则:

如果在成员内部类中,需要访问外部类的非静态成员,那么这个成员内部类,就只能声明为非静态成员内部类。

如果在成员内部类中,需要访问外部类的非静态成员,,那么这个成员内部类,就可以声明为非静态成员内部类,用可以声明为静态内部类。但是声明为静态内部类更简洁。

2.6 面向对象开发原则:高内聚低耦合

首先:第2.4小节中MyArrayList和SingleLink类中都有内部类Itr,它们的方法,以及作用是相同的,就应该为它们抽取共同的父类或父接口。例如:

public interface MyIterator {
    boolean hasElement();
    Object getElementAndMoveNext();
}

另外,在测试类TestMyArrayListItr 和 TestSingleLinkItr类中,直接访问MyArrayList和SingleLink类的内部类是不符合高内聚低耦合的开发原则的,

MyArrayList.Itr iterator = list.new Itr();

如上写法的代码也非常怪异,应该避免,解决办法是,在外部类中提供一个方法,用于返回成员内部类的对象。例如:public MyIterator iterator()

示例代码
迭代器接口MyIterator
​
package com.atguigu.inner.oop;

public interface MyIterator {
    boolean hasElement();
    Object getElementAndMoveNext();
}
MyArrayList动态数组类(再升级)
package com.atguigu.inner.oop;

public class MyArrayList {//动态数组容器类
    private Object[] all = new Object[4];
    private int total;//默认值是0

    public void add(Object obj) {
        if (total >= all.length) {//数组已满,需要扩容
            //创建一个新数组,长度是原来数组的2倍
            Object[] newArr = new Object[all.length * 2];
            //复制total个元素到新数组中
            for (int i = 0; i < total; i++) {
                newArr[i] = all[i];
            }
            //all指向新数组,扔掉旧数组
            all = newArr;
        }
        //把新元素放到[total]位置,total自增1,代表元素个数增加,同时也代表下一个元素的位置后移
        all[total++] = obj;
    }

    @Override
    public String toString() {
        String str = "[";
        for (int i = 0; i < total; i++) {
            str += all[i] + (i == total - 1 ? "]" : ",");
        }
        return str;
    }

/*    public class Itr{//非静态的成员内部类
        int index;//默认值是0

        public boolean hasElement(){
            return index < total;
        }

        public Object getElementAndMoveNext(){
            return all[index++];
        }
    }*/

    private class Itr implements MyIterator {//非静态的成员内部类
        int index;//默认值是0

        public boolean hasElement(){
            return index < total;
        }

        public Object getElementAndMoveNext(){
            return all[index++];
        }
    }

    public MyIterator iterator(){
        return new Itr();
    }
}
SingleLink单链表类(再升级)
package com.atguigu.inner.oop;

public class SingleLink {//单链表
    private SingleNode first;//单链表的第一个结点

    //静态内部类,这个内部类在别的地方没用,仅限于当前外部类使用,所以将它声明为private
    private static class SingleNode{//结点
        //属性有俩:(1)元素(2)下一个结点
        Object element;
        SingleNode next;

        SingleNode(Object element, SingleNode next) {
            this.element = element;
            this.next = next;
        }
    }

    public void add(Object obj){
        //(1)创建结点对象,用来包裹我们的元素对象obj
        SingleNode newNode = new SingleNode(obj, null);
        //这里null代表新结点没有下一个结点

//        (2)分情况讨论
        if(first == null){//链表是空的
            first = newNode;//当前结点就是链表的第一个结点
        }else{
            //找到最后一个
            SingleNode last = findLast();
            //让把新结点连接到最后一个结点的后面
            last.next = newNode;
        }
    }

    private SingleNode findLast(){
        SingleNode node = first;
        while(node.next != null){
            node = node.next;
        }
        return node;
    }

    @Override
    public String toString() {
        String str = "[";
        SingleNode node = first;
        while(node.next != null){
            str += node.element +",";//拼接结点的元素
            node = node.next;
        }
        //出了循环node是代表最后一个结点,它的元素没有拼接到str中
        str += node.element +"]";
        return str;
    }

    /*public class Itr{
        SingleNode node = first;//默认指向单链表的第一个结点

        public boolean hasElement(){
            return node != null;
        }

        public Object getElementAndMoveNext(){
            Object element = node.element;
            node = node.next;
            return element;
        }
    }*/

    //遵循了一个开发原则:高内聚,低耦合
    private class Itr implements MyIterator {
        SingleNode node = first;//默认指向单链表的第一个结点

        public boolean hasElement(){
            return node != null;
        }

        public Object getElementAndMoveNext(){
            Object element = node.element;
            node = node.next;
            return element;
        }
    }

    public MyIterator iterator(){
        return new Itr();
    }
}
测试类
package com.atguigu.inner.oop;

public class TestItr {
    public static void main(String[] args) {
        MyArrayList list = new MyArrayList();
        list.add("张三");
        list.add("李四");
        list.add("王五");

//        MyIterator m1 = list.new Itr();
        MyIterator m1 = list.iterator();
        while(m1.hasElement()){
            Object element = m1.getElementAndMoveNext();
            System.out.println(element);
        }

        SingleLink link = new SingleLink();
        link.add("张三");
        link.add("李四");
        link.add("王五");
        link.add("赵六");

//        MyIterator m2 = link.new Itr();
        MyIterator m2 = link.iterator();
        while(m2.hasElement()){
            Object element = m2.getElementAndMoveNext();
            System.out.println(element);
        }
    }
}

2.7 成员内部类与外部类的关系

1、作为类的角色来说:没啥特殊的

首先,无论是成员内部类(甚至包括局部内部类、匿名内部类),还是外部类,它们都是类,都有自己独立的字节码文件。即都需要独立加载。

  • 匿名内部类:外部类名$数字编号.class

  • 局部内部类:外部类名$数字编号局部内部类名.class

  • 成员内部类:外部类名$成员内部类名.class

其次,四种内部类都可以创建对象

第三,都可以包含自己的类成员。

  • 成员变量

  • 构造器

  • 代码块(很少 )

  • 成员方法

  • 内部类(虽然理论上内部类还可以套内部类,但是实际没有人这么做)

第四,它们都可以有自己的父类,自己的父接口。

2、成员内部类作为外部类的成员来说

第一:权限修饰符

外部类的修饰符:public或缺省

成员内部类的修饰符:4种之一(public、protected、缺省、private)

第二:其他修饰符

外部类的其他修饰符:final、abstract

成员内部类的其他修饰符:final、abstract、static

第三:成员内部类和外部类之间可以互相使用对方的私有成员,它们是互相信任的关系。

但是需要遵守,同一个的静态成员不能直接访问非静态成员。

外部类访问成员内部类的成员时:

  • 静态的:成员内部类名.静态成员

  • 非静态:先创建成员内部类名的对象,然后对象名.非静态成员

第四:在外部类的外面,使用成员内部类时,需要依赖于外部类。

对于非静态内部类来说,创建它的对象,还依赖于外部类的对象。

示例代码
外部类和内部类的代码
package com.atguigu.inner.member;

public class Outer {
    private static int outA;
    private int outB;

    private int c = 1;

    public static class InnerOne{
        private static int inA;
        private int inB;

        public void inMethod(){
            System.out.println("outA = " + outA);
//            System.out.println("outB = " + outB);
//          InnerOne类作为Outer的静态成员,不能访问领一个非静态成员outB
        }

        public static void inFun(){
            System.out.println("静态内部类的静态方法");
        }
    }

    public class InnerTwo{
        private static int inA;
        private int inB;
        private int c = 2;

        public void method(){
            System.out.println(this);//InnerTwo的对象
            System.out.println(Outer.this);//Outer的对象

            System.out.println("内部类c = " + this.c);
            System.out.println("外部类c = " + Outer.this.c);
        }

        public void inMethod(){
            System.out.println("outA = " + outA);
            System.out.println("outB = " + outB);
        }

        public static void inFun(){
            System.out.println("静态内部类的静态方法");
        }
    }

    public InnerTwo getOuterTwo(){//方法名可以自己命名
        return new InnerTwo();
    }

    public void outMethod(){
        System.out.println("InnerOne.inA = " + InnerOne.inA);
//        System.out.println("InnerOne.inB = " + InnerOne.inB);
        //对于外部类Outer的outMethod()来说,可以直接访问InnerOne这个静态成员
        //但是,你想要访问InnerOne里面的非静态成员inB,需要InnerOne类的对象
        InnerOne one = new InnerOne();
        System.out.println("InnerOne.inB = " + one.inB);
    }
}
测试类的代码
package com.atguigu.inner.member;

public class TestOuter {
    public static void main(String[] args) {
        //访问InnerOne类的inMethod()方法
        Outer.InnerOne inObj = new Outer.InnerOne();
        inObj.inMethod();

        //访问InnerOne类的inFun()方法
        Outer.InnerOne.inFun();


        System.out.println("=================");
        //访问InnerTwo类的inMethod()方法
        //首先,需要InnerTwo的对象
        //但是,创建InnerTwo对象时,又依赖于外部类Outer的对象
        Outer outer = new Outer();
        Outer.InnerTwo inObj2 = outer.new InnerTwo();
        inObj2.inMethod();
        inObj2.method();

        //访问InnerTwo类的inFun()方法
        //InnerTwo类的全名称:Outer.InnerTwo
        Outer.InnerTwo.inFun();

        System.out.println("=====================");
        //实际开发中,会避免在外部类的外面去创建内部类的对象
        //而是改为由外部类的某个方法,提供内部类的对象
        Outer out = new Outer();
        Outer.InnerTwo inObj3 = out.getOuterTwo();
    }
}

三、枚举类

3.1 什么是枚举类

枚举类是指一种特殊的类,它的特殊之处在于 它的对象是固定的有限的几个常量对象,不能随意创建它的对象,只是用它提前创建好的对象。

例如:星期,一共有7个对象,从7个对象选择使用。

月份,一共有12个对象,从12个对象选择使用。

3.2 如何定义枚举类

3.2.1 JDK5之前(麻烦)

需求:

定义一个Week星期类,限定它的对象是固定的7个对象。

思考:

  • 如何限制在枚举类的外面new对象?构造器私有化

  • 如何创建“固定的”7个对象?在枚举类的内部,提前创建好7个对象

package com.atguigu.inner.meiju;

//转大小写快捷键:Ctrl + Shift + U
public class Week {
    //7个常量对象
   public static final Week MONDAY = new Week();
   public static final Week TUESDAY = new Week();
   public static final Week WEDNESDAY = new Week();
   public static final Week THURSDAY = new Week();
   public static final Week FRIDAY = new Week();
   public static final Week SATURDAY = new Week();
   public static final Week SUNDAY = new Week();

    private Week(){

    }

    public void show(){
        if(this == MONDAY){
            System.out.println("星期一");
        }else if(this == TUESDAY){
            System.out.println("星期二");
        }
        //剩下的省略了
    }
}
package com.atguigu.inner.meiju;

public class TestWeek {
    public static void main(String[] args) {
//        Week w = new Week();//无法创建枚举类Week的对象,因为构造器私有化了
        Week w = Week.MONDAY;
        w.show();
    }
}

 

3.2.2 JDK5之后(简化,推荐)

【修饰符】 enum 枚举类名{
    常量对象列表;
    
    //枚举类的其他成员
}

注意:

  • 如果枚举类除了常量对象列表,没有其他成员的话,常量对象列表后面的; 可以省略,否则不能省略。常量对象列表必须在枚举类的首行。

  • 枚举类型可以有其他的成员:属性、构造器、方法、代码块、内部类(代码块与内部类很少用)。

  • 枚举类的构造器,一定是私有的。

  • 枚举类的属性“通常或建议”是final的。因为枚举类的对象都是常量对象,那么常量的属性一般也不建议修改。

  • 枚举类没有子类,因为枚举类构造器都是private,子类无法调用父类构造器。

  • 枚举类也不能指定父类。因为枚举类有一个固定的直接父类:java.lang.Enum类,不过根父类仍然是Object。

示例代码
颜色枚举类Color
package com.atguigu.inner.meiju;

//enum是一种特殊的class

public enum Color {
    WHITE(255,255,255), //默认调用自己的有参构造
    BLACK, //调用自己的无参构造
    RED(255,0,0,"红色"),
    GREEN(0,255,0),
    BLUE(0,0,255);

    //枚举类的属性建议是final
    private final int r;
    private final int g;
    private final int b;
    private String description;//不是final也是可以,不会报错

     Color(){//省略了private
        this.r = 0;
        this.g = 0;
        this.b = 0;
    }

    Color(int r, int g, int b) {//省略了private
        this.r = r;
        this.g = g;
        this.b = b;
    }

    Color(int r, int g, int b,String description) {//省略了private
        this.r = r;
        this.g = g;
        this.b = b;
        this.description = description;
    }

    public int getR() {
        return r;
    }

    public int getG() {
        return g;
    }

    public int getB() {
        return b;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    @Override
    public String toString() {
        return "Color{" +
                "r=" + r +
                ", g=" + g +
                ", b=" + b +
                ", name ='" + name() + '\'' +
                '}';
    }
}
测试类
package com.atguigu.inner.meiju;

public class TestColor {
    public static void main(String[] args) {
//        Color c = new Color();//不能创建枚举类的对象,构造器私有化
        Color red = Color.RED;
        System.out.println(red.getR());
        System.out.println(red.getG());
        System.out.println(red.getB());
        System.out.println(red.getDescription());
        red.setDescription("大红色");
        System.out.println(red.getDescription());
    }
}

3.3 枚举类支持的方法和操作

1、JDK5开始switch开始支持枚举类类型的常量对象

2、枚举类从父类Enum类中继承了一些方法

  • public String toString():返回了常量对象名称。当然你的枚举类中,也可以对其进行再次重写。

  • public String name():返回了常量对象的名称。

  • public int ordinal():返回了常量对象的序号或下标。

  • public int compareTo(枚举类型的对象):比较两个枚举类对象的大小。返回值是正整数、负整数、0,表示大于,小于、等于的关系。

3、编译器还会给枚举类增加如下两个静态方法

  • public static 枚举类型[] values()

  • public static 枚举类型 valueOf(String name)

示例代码1

 

package com.atguigu.inner.meiju;

public class TestColor2 {
    public static void main(String[] args) {
        //先获取一个枚举类Color的常量对象
        Color c = Color.BLUE;
        switch (c){
            case WHITE :
                System.out.println("白色代表纯洁");
                break;
            case BLACK:
                System.out.println("黑色代表神秘");
                break;
            case RED:
                System.out.println("红色代表喜庆");
                break;
            case GREEN:
                System.out.println("绿色代表生命");
                break;
            case BLUE:
                System.out.println("蓝色代表忧郁");
                break;
        }
    }
}

示例代码2

package com.atguigu.inner.meiju;

public class TestColor3 {
    public static void main(String[] args) {
        //先获取一个枚举类Color的常量对象
        Color c1 = Color.BLUE;
        System.out.println(c1);
        System.out.println(c1.name());
        System.out.println(c1.ordinal());//4

        System.out.println("=============");
        Color[] all = Color.values();
        for (int i = 0; i < all.length; i++) {
            System.out.println(all[i]);
        }

        System.out.println("=============");
        Color c2 = Color.valueOf("RED");
        System.out.println(c2);

        System.out.println("==================");
//        System.out.println(c1 > c2);//错误,地址值无法比较大小
        System.out.println(c1.compareTo(c2));//2
        //返回值是正整数,表示c1 > c2
    }
}

四、包装类

4.1 什么是包装类

Java为每一种基本数据类型都提供了对应的包装类

基本数据类型包装类(java.lang)
1byteByte
2shortShort
3intInteger
4longLong
5floatFloat
6doubleDouble
7charCharacter
8booleanBoolean

4.2 为什么要用包装类

Java是面向对象的编程语言,很多的API或新特性都是针对 “引用数据类型,即对象”来设计的,不支持基本数据类型。例如:集合、泛型等。

所以,Java必须为8种数据类型设计对应包装类。

包装类是对象,不支持运算符。

4.3 装箱与拆箱

  • 装箱:把基本数据类型的值转换为(包装为)包装类的对象。

  • 拆箱:把包装类的对象转换为(拆解为)基本数据类型的值。

  • JDK1.5之后,支持自动装箱和拆箱,但是只能对应类型之间支持自动装箱与拆箱。

package com.atguigu.inner.wrapper;

public class TestBoxingAndUnBoxing {
    public static void main(String[] args) {
        int a = 1;
        Integer i = a;//自动装箱,类似于  Integer i = new Integer(a);
        //i是一个对象,而a是一个基本数据类型的变量

        Integer j = Integer.valueOf(1);
        int b = j; //自动拆箱
        //j是一个对象,b是一个基本数据类型的变量

       // Double d = 1;//报错,因为1是int类型,d是Double,它们不是对应类型
        Double d = 1.0;
        Double num1 = 1D;//在数字后面加D或d,表示double类型
//        Double num2 = 1F;//在数字后面加F或f,表示float类型
    }
}

4.4 包装类的特点

4.4.1 包装类对象不可变(面试题常见)

结论:包装类对象一旦修改,就指向新对象。并不会修改原对象。

package com.atguigu.inner.wrapper;

public class TestWrapper {
    public static void main(String[] args) {
        int x = 1;
        Integer obj = new Integer(1);

        change(x,obj);

        System.out.println("x = " + x);//x=1
        System.out.println("obj = " + obj);//obj=1
    }

    public static void change(int a, Integer i){
        a++;
        i++;//只要修改就会产生新对象
    }
}

4.4.2 部分包装类对象可以共享

因为包装类对象不可变,那么Java中就把常用的一些包装类对象进行缓存,用于共享,这样可以节省内存,减少对象的数量。

包装类缓存对象范围,不是说数据范围
Byte[-128 ,127]
Short[-128 ,127]
Integer[-128 ,127]
Long[-128 ,127]
Float
Double
Character[0,127]
Booleantrue,false

结论:

  • 必须在共享对象范围内

  • 必须是自动装箱或valueOf方法得到的对象才会共享。

  • 直接new的不共享。

package com.atguigu.inner.wrapper;

public class TestShare {
    public static void main(String[] args) {
        Integer a = 1;
        Integer b = 1;
        System.out.println(a == b);//true,[-128,127]之间可以共享
        //这里比较的是两个对象的首地址

        Integer c = 200;
        Integer d = 200;
        System.out.println(c == d);//false,超过[-128,127]范围是不共享

        Integer e = new Integer(1);
        Integer f = new Integer(1);
        System.out.println(e == f);//false,因为这里明确是new了两个对象
        //这个构造器已经被废弃了,在JDK9之后废弃了。因为它违反了共享的原则。

        Integer h = Integer.valueOf(1);
        Integer k = Integer.valueOf(1);
        System.out.println(h == k);//true
    }
}

4.4.3 包装类对象会自动确定该不该拆箱

  • 一个包装类一个基本数据类型计算,就会拆箱。

  • 两个包装类使用了+,>,等运算符,就会拆箱。

  • 两个包装类使用==,!=,不会拆箱,因为此时是按照地址值处理的。

  • package com.atguigu.inner.wrapper;
    
    public class TestUnBoxing {
        public static void main(String[] args) {
            Integer a = 1;
            Integer b = 2;
            System.out.println(a + b);//自动拆箱。因为包装类对象不支持运算符+,一旦遇到这种情况,就会自动拆箱
            System.out.println(a < b);//自动拆箱。因为包装类对象不支持运算符<,一旦遇到这种情况,就会自动拆箱
    
            double d = 1.0;
            System.out.println(a == d);//自动拆箱。因为Integer与double,一个是对象,一个是基本数据类型,会拆箱计算
                                        //int与double类型比较
            Double e = 1.0;
            //System.out.println(a == e);//报错,因为a和e都是对象,只能按照对象的规则进行
                                        //两个不同类型的对象是无法比较地址值
        }
    }
    

4.5 包装类的部分常用方法

package com.atguigu.inner.wrapper;

public class TestAPI {
    public static void main(String[] args) {
        System.out.println(Integer.MAX_VALUE);//获取int的最大值
        System.out.println(Integer.MIN_VALUE);//获取int的最小值

        System.out.println(Long.MAX_VALUE);
        System.out.println(Long.MIN_VALUE);

        System.out.println("=====================");
        String str1 = "123";
        String str2 = "568";
        System.out.println("实现拼接:" + (str1 + str2));//实现拼接:123568
        int a = Integer.parseInt(str1);
        int b = Integer.parseInt(str2);
        System.out.println("实现求和:" + (a+b));//实现求和:691

        System.out.println("=====================");
        String str3 = "56.36";
        String str4 = "3.14";
        double c = Double.parseDouble(str3);
        double d = Double.parseDouble(str4);
        System.out.println(c + d);//59.5

        System.out.println("=====================");
        String str5 = "hello";
        char letter1 = str5.charAt(0);
        char letter2 = str5.charAt(1);
        System.out.println("letter1 = " + letter1);//letter1 = h
        System.out.println("letter2 = " + letter2);//letter2 = e

        System.out.println("===================");
        int e = 123;
        String s = e + "";//拼接上一个空字符串,就可以把int的值转为String的值
        String s2 = String.valueOf(e);

        System.out.println("====================");
        int x = 1;
        int y = 2;
        System.out.println(x > y);//boolean类型的false
        System.out.println(Integer.compare(x,y));//-1 表示 x<y的意思

        System.out.println("==================");
        double m = 1.4;
        double n = 1.2;
        System.out.println(Double.compare(m, n));//1 表示m > n

        System.out.println("=====================");
        char lowerletter = 'a';
        char upperLetter = (char)(lowerletter - 32);
        char upperLetter2 = Character.toUpperCase(lowerletter);
        System.out.println(lowerletter +"对应大写" + upperLetter);
        System.out.println(lowerletter +"对应大写" + upperLetter2);

        System.out.println("=====================");
        char t = 'A';
        char lowerT= (char)(t+32);
        char lowerT2 = Character.toLowerCase(t);
        System.out.println(t +"对应的小写字母:" + lowerT);
        System.out.println(t +"对应的小写字母:" + lowerT2);

        System.out.println("=============");
        char z = 'a';
        char xiao1 = (char)(z+32);//有风险,当编码值已经是小写字母,就会有问题
        char xiao2 = Character.toLowerCase(z);//安全
        System.out.println(z+"对应的小写字母:" + xiao1);
        System.out.println(z+"对应的小写字母:" + xiao2);
    }
}

  • 14
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值