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

news2024/9/25 11:21:00

一、内部类(续)

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);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1615774.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

力扣刷题 70.爬楼梯

题干 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢&#xff1f; 示例 1&#xff1a; 输入&#xff1a;n 2 输出&#xff1a;2 解释&#xff1a;有两种方法可以爬到楼顶。 1. 1 阶 1 阶 2. 2 阶 示例 2&…

HarmonyOS开发实例:【图片编辑应用】

介绍 本篇Codelab通过动态设置元素样式的方式&#xff0c;实现几种常见的图片操作&#xff0c;包括裁剪、旋转、缩放和镜像。效果如图所示&#xff1a; 相关概念 [image组件]&#xff1a;图片组件&#xff0c;用来渲染展示图片。[div组件]&#xff1a;基础容器组件&#xff0…

PLC_博图系列☞N=:在信号下降沿置位操作数

、 PLC_博图系列☞N&#xff1a;在信号下降沿置位操作数 文章目录 PLC_博图系列☞N&#xff1a;在信号下降沿置位操作数背景介绍N&#xff1a; 在信号下降沿置位操作数说明参数示例 关键字&#xff1a; PLC、 西门子、 博图、 Siemens 、 N 背景介绍 这是一篇关于PLC编程的…

Python网络数据抓取(3):Requests

引言 在这一部分&#xff0c;我们将探讨Python的requests库&#xff0c;并且利用这个库来进行网页数据抓取。那么&#xff0c;我们为何需要这个库&#xff0c;以及怎样利用它呢&#xff1f; requests库是广受大家欢迎的一个库&#xff0c;它是下载次数最多的。这个库使我们能够…

C语言学习/复习27----sizeof/strlen/数组/指针

一、数组笔试题目解析 1.一维数组 1.sizeof()操作符与int数组 注意事项1&#xff1a;sizeof()依据类型推断大小 注意事项2&#xff1a;注意区分是( )内是地址还是普通元素类型 注意事项3&#xff1a;&#xff08;&#xff09;内是单独的数组名时计算整个数组的大小&#xff0c;…

海外服务器被恶意攻击怎么办

如果您的海外服务器遭受了恶意攻击&#xff0c;以下是一些应对措施和步骤&#xff0c;立即隔离服务器。如果您察觉到服务器受到恶意攻击&#xff0c;立即隔离服务器&#xff0c;将其与网络隔离&#xff0c;以防止攻机进一步扩散。通知服务器提供商&#xff0c;以便他们能够提供…

有了可视化工具,你定制设计得瑟瑟发抖了吧,其实你想多了。

目前市面上有N多可视化的工具&#xff0c;可以做成可视化大屏&#xff0c;甚至有很多B端系统也附带可视化页面&#xff0c;据此就有很多人开始怀疑我们这些做定制开发的&#xff0c;还有啥生存空间。 其实你真的多虑了&#xff0c;存在即合理&#xff0c;我们承认可视化工具的标…

小白必备:Python必须掌握的十大模块,建议收藏!

前言 Python 是一种高级、解释型和通用动态编程语言&#xff0c;侧重于代码的可读性。 它在许多组织中使用&#xff0c;因为它支持多种编程范例。 它还执行自动内存管理。 它是世界上最受欢迎的编程语言之一。 这是有很多原因的&#xff1a; 这很容易学习。它超级多才多艺。…

05集合-CollectionListSet

Collection体系的特点、使用场景总结 如果希望元素可以重复&#xff0c;又有索引&#xff0c;索引查询要快? 用ArrayList集合, 基于数组的。(用的最多) 如果希望元素可以重复&#xff0c;又有索引&#xff0c;增删首尾操作快? 用LinkedList集合, 基于链表的。 如果希望增…

【电机控制】滑模观测器PMSM无感控制波形图

【电机控制】滑模观测器PMSM无感控制波形图 文章目录 前言一、FOC控制1.三相电流2.Clark变换静止坐标系iαiβ3.park变换旋转坐标系idiq4.电流环PI控制输出UdUq5.UdUq 反park变换UαUβ 二、反电动势观测器BEMF1.静止坐标系iαiβ提取反电动势EaEb2.反电动势EaEb提取位置信息、…

【国信华源参加全国地质灾害防治新技术新方法新设备交流会】

4月17-18日&#xff0c;以“提升地质灾害防治能力 服务保障高质量发展”为主题&#xff0c;由中国地质灾害防治与生态修复协会主办、云南地质工程第二勘察院有限公司承办的“全国地质灾害防治新技术新方法新设备成果交流会”在云南昆明圆满召开。会议特邀中国工程院院士等知名…

实现游戏地图读取与射击运行

射击代码来源自2D 横向对抗射击游戏&#xff08;by STF&#xff09; - CodeBus 地图读取改装自 瓦片地图编辑器 解决边界检测&#xff0c;实现使用不同像素窗口也能移动不闪退-CSDN博客 // 程序&#xff1a;2D RPG 地图编辑器改游戏读取器 // 作者&#xff1a;民用级脑的研发…

【电控笔记6.3】采样-Z转换-零阶保持器

本质 数字转模拟:零阶保持器 采样 z-1所描述的物理意义即为延迟T时间的拉氏转换e-sT 信号采样延时

stable diffusion本地部署@win10

一键无脑安装stable-diffusion-webui stable diffusion是当前非常出色的文生图模型&#xff0c;要优于以前gan文生图模型。现在有了stable-diffusion-webui软件&#xff0c;可以一键安装&#xff0c;大大简化了操作难度。本文档就是stable-diffusion-webui在windows 10上的安装…

UI5:面向企业级应用的JavaScript框架

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

OpenTelemetry-1.介绍

目录 1.是什么 2.为什么使用 OpenTelemetry 3.数据类型 Tracing Metrics Logging Baggage 4.架构图 5.核心概念 6.相关开源项目 ​编辑 7.分布式追踪的起源 8.百花齐放的分布式追踪 Zipkin Skywalking Pinpoint Jaeger OpenCensus OpenTracing 9.Openteleme…

Spring Boot入门(20):轻松搞定多数据源配置,Spring Boot与Mybatis-Plus的完美结合!

前言 本文将介绍如何在Spring Boot框架下使用mybatis-plus实现多数据源配置。多数据源配置是一个常见的需求&#xff0c;在实际项目中也经常遇到&#xff0c;因此掌握多数据源配置的技巧是非常重要的。 摘要 本文将为大家介绍如何使用Spring Boot和mybatis-plus实现多数据源…

学之思考试系统环境启动QA

学之思考试系统环境启动Q&A 目录 学之思考试系统环境启动Q&A后台代码启动失败:前台代码启动失败常见解决方式参考资料后台代码启动失败: 后端代码启动不成功,不能够自动导入maven,配置依赖; 使用idea打开到:\xzs-master\xzs-mysql-master\source\xzs这个路径下;…

小心中伏!伦敦银出入金有要求的

伦敦银是采用了t0资金回转制度的投资品种&#xff0c;所以投资者在交易实现了盈利之后&#xff0c;可以当天立马就选择把盈利转出&#xff0c;当然如果投资者参与了平台的营销活动&#xff0c;申请出金的行为&#xff0c;就有可能导致活动资格被取消&#xff0c;对此投资者应该…

基于SSM+Vue的护工预约服务小程序和后台管理系统

1、系统演示视频&#xff08;演示视频&#xff09; 2、需要请联系