一、内部类(续)
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) | |
|---|---|---|
| 1 | byte | Byte | 
| 2 | short | Short | 
| 3 | int | Integer | 
| 4 | long | Long | 
| 5 | float | Float | 
| 6 | double | Double | 
| 7 | char | Character | 
| 8 | boolean | Boolean | 
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] | 
| Boolean | true,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);
    }
}



















