一、内部类(续)
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);
}
}