Java的特殊类型
对Java中一些特殊类型,抽象类,接口,包装类,枚举,注解的简要介绍
Author: Msuenb
Date: 2023-02-11
抽象类
当我们声明一个几何图形类:圆、矩形、三角形类等,发现这些类都有共同特征:求面积、求周长、获取图形详细信息。那么这些共同特征应该抽取到一个公共父类中。但是这些方法在父类中又无法给出具体的实现,而是应该交给子类各自具体实现。那么父类在声明这些方法时,就只有方法签名,没有方法体,我们把没有方法体的方法称为抽象方法。Java语法规定,包含抽象方法的类必须是抽象类。
语法格式
- 抽象方法:被
abstract
修饰发没有方法体的方法。 - 抽象类:被
abstract
修饰的类。
抽象类语法格式
【权限修饰符】 abstract class 类名 {}
【权限修饰符】 abstract class 类名 extends 父类名 {}
抽象方法语法格式
【权限修饰符】 abstract 返回值类型声明 方法名(【形参列表】);
示例:
abstract class Animal {
public abstract void eat();
}
class Cat extends Animal {
@Override
public void eat() { // 需要重写父类的eat方法
System.out.println("猫咪吃小鱼干...");
}
}
此时的方法重写,是子类对父类抽象方法的完成实现,我们将这种方法重写的操作,也叫做实现方法。
抽象类使用细节
- 抽象类不能创建对象,只能创建其非抽象子类的对象。
- 抽象类中,也有构造方法,是供子类创建对象时,初始化父类成员变量使用的。
- 抽象类中,不一定包含抽象方法,但包含抽象方法的类一定是抽象类。
- 抽象类的子类必须重写父类所有抽象方法,除非该子类也是抽象类。
public class AbstractDetail {
public static void main(String[] args) {
Cat cat = new Cat("招财", 1);
cat.eat();
cat.cry();
Dog dog = new Dog("旺财", 2);
dog.eat();
dog.cry();
// Animal a = new Animal(); 抽象类不能实例化
Animal cat1 = new Cat(); // 可以使用多态方式调用Animal中的方法
cat1.method();
}
}
abstract class Animal {
String name;
int age;
public Animal() {
}
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
public abstract void eat();
public abstract void cry();
void method() {
System.out.println("抽象类中可以有非抽象方法");
}
}
class Cat extends Animal {
public Cat() {
// super(); 隐含
}
public Cat(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println("猫咪吃小鱼干...");
}
@Override
public void cry() {
System.out.println("猫咪喵喵叫...");
}
}
class Dog extends Animal {
public Dog() {
// super(); 隐含
}
public Dog(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println("小狗吃骨头干...");
}
@Override
public void cry() {
System.out.println("小狗汪汪叫...");
}
}
abstract class A extends Animal {
@Override
public void eat() { // 实现部分父类的抽象方法
}
}
class B extends A {
@Override
public void cry() { // 仍要实现父抽象类没有实现的cry方法
}
}
修饰符一起使用问题
外部类 | 成员变量 | 代码块 | 构造器 | 方法 | 局部变量 | 内部类 | |
---|---|---|---|---|---|---|---|
public | √ | √ | × | √ | √ | × | √ |
protected | × | √ | × | √ | √ | × | √ |
缺省 | √ | √ | × | √ | √ | × | √ |
private | × | √ | × | √ | √ | × | √ |
static | × | √ | √ | × | √ | × | √ |
final | √ | √ | × | × | √ | √ | √ |
abstract | √ | × | × | × | √ | × | √ |
native | × | × | × | × | √ | × | × |
不能和abstract一起使用的修饰符:
-
abstract和final不能一起修饰方法和类
-
abstract和static、private、native不能一起修饰方法
static和final一起使用:
-
修饰方法:可以,因为都不能被重写
-
修饰成员变量:可以,表示静态常量;修饰局部变量:不可以,static不能修饰局部变量
-
修饰代码块:不可以,final不能修改代码块
-
修饰内部类:可以一起修饰成员内部类,不能一起修饰局部内部类
接口
详见:接口
包装类
Java
提供了两个类型系统,基本类型与引用类型,使用基本类型在于效率,然而当要使用只针对对象设计的API
或新特性(例如泛型),那么基本数据类型的数据就需要用包装类来包装。
序号 | 基本数据类型 | 包装类(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 |
9 | void | Void |
自动装箱与拆箱
装箱:把基本数据类型转为包装类对象。为了使用专门为对象设计的API和特性
拆箱:把包装类对象拆为基本数据类型。基本数据类型方便运算
-
基本数值 => 包装对象
Integer i1 = new Integer(4); // 使用构造函数 Integer i2 = Integer.valueOf(4); // 使用包装类中的valueOf方法
-
包装对象 => 基本数值
Integer i1 = new Integer(4); int num1 = i1.intValue();
-
自动装箱与拆箱(
JDK1.5
之后)Integer i = 4; //自动装箱 相当于Integer i = Integer.valueOf(4); int num = i + 5; // 自动拆箱(将i转换为基本数值) 相当于int num = i.intValue() + 5; i = num; // 再次装箱,把基本数值转成对象。 Double d = 1; // 错误的,1是int类型
包装类一些API
基本数据类型与字符串之间的转换:
-
基本数据类型 => 字符串
int a = 10; String str = a + ""; String s = Integer.toString(a); String s = String.valueOf(a);
-
字符串 => 基本数据类型
String s = "10"; int a = Integer.valueOf(s); int b = Integer.parseInt(s);
字符大小写转换:
-
Character.toUpperCase('a'); // 转大写 Character.toLowerCase('A'); // 转小写
包装类对象特点
包装类对象不可变:
Integer a = 1;
a = a + 1;
System.out.println("a = " + a);
// 这类Integer等包装类对象是“不可变”对象,即一旦修改,就是新对象,和实参就无关了
包装类缓存对象:
包装类 | 缓存对象 |
---|---|
Byte | -128~127 |
Short | -128~127 |
Integer | -128~127 |
Long | -128~127 |
Float | 没有 |
Double | 没有 |
Character | 0~127 |
Boolean | true和false |
Integer a = 1;
Integer b = 1;
System.out.println(a == b);// true 两个用的都是缓存的常量对象,在方法区
Integer i = 128;
Integer j = 128;
System.out.println(i == j);// false 没有缓存128
Integer m = new Integer(1);// 新new的在堆中
Integer n = 1; // 这个用的是缓存的常量对象
System.out.println(m == n);// false
Double d1 = 1.0;
Double d2 = 1.0;
System.out.println(d1 == d2);// false 没有缓存对象
包装类对象计算问题:
- 两个包装类对象之间进行
==
和!=
运算,比较的是对象的地址值,要求两个包装类对象必须是同一种类型。 - 两个包装类对象之间进行其他运算(
>,<,+,-...
),先自动拆箱为基本数据类型之后再运算,不要求两个包装类对象是同一种类型(Boolean不参与)。 - 包装类对象和基本数据类型的混合运算都是自动拆箱为基本数据类型之后再运算。
枚举
某些类型的对象是有限的几个,这样的例子举不胜举:
- 星期:Monday、Tuesday、Wednesday、Thursday、Friday、Saturday、Sunday
- 月份:January、February、March、April、May、June、August...、December
- 季节:Spring、Summer、Autumn、Winter
枚举类型本质上也是一种类,只不过是这个类的对象是固定的几个,而不能随意让用户创建。
enum声明枚举
【修饰符】 enum 枚举类名{
常量对象列表
}
【修饰符】 enum 枚举类名{
常量对象列表;
其他成员列表;
}
示例:
public class Main {
public static void main(String[] args) {
Week monday = Week.MONDAY;
System.out.println(monday);
}
}
enum Week {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY;
}
枚举类的要求和特点
- 枚举类的常量对象列表必须在枚举类的首行,因为是常量,所以建议大写。
- 如果常量对象列表后面没有其他代码,那么“;”可以省略,否则不可以省略“;”。
- 编译器给枚举类默认提供的是private的无参构造,如需有参构造要手动定义,有参构造的private可以省略,调用有参构造:
常量名(实参列表)
。 - 枚举类型如有其它属性,建议(不是必须)这些属性也声明为final的,因为常量对象在逻辑意义上应该不可变。
示例代码:
public class Main {
public static void main(String[] args) {
Week week = Week.MONDAY;
System.out.println(week);
switch(week) {
case MONDAY:
System.out.println("怀念周末,困意很浓");break;
case TUESDAY:
System.out.println("进入学习状态");break;
case WEDNESDAY:
System.out.println("死撑");break;
case THURSDAY:
System.out.println("小放松");break;
case FRIDAY:
System.out.println("又信心满满");break;
case SATURDAY:
System.out.println("开始盼周末,无心学习");break;
case SUNDAY:
System.out.println("一觉到下午");break;
}
}
}
enum Week {
MONDAY("星期一"),
TUESDAY("星期二"),
WEDNESDAY("星期三"),
THURSDAY("星期四"),
FRIDAY("星期五"),
SATURDAY("星期六"),
SUNDAY("星期日");
// private Week(String s) {} // 有参构造 可以省略private
// 但这样 s 没有意义了
private final String description;
Week(String description){ // private 省略
this.description = description;
}
public String getDescription() {
return description;
}
@Override
public String toString() {
return super.toString() +":"+ description;
}
}
枚举类型常用方法
1.String toString(): 默认返回的是常量名(对象名),可以继续手动重写该方法!
2.String name():返回的是常量名(对象名)
3.int ordinal():返回常量的次序号,默认从0开始
4.枚举类型[] values():返回该枚举类的所有的常量对象,返回类型是当前枚举的数组类型,是一个静态方法
5.枚举类型 valueOf(String name):根据枚举常量对象名称获取枚举对象
示例:
String name = week.name();
System.out.println(name);
int ordinal = week.ordinal();
System.out.println(ordinal);
Week[] values = Week.values();
for (Week value : values) {
System.out.println(value);
}
Week monday = Week.valueOf("MONDAY");
System.out.println(monday);
注解
这些内容了解即可
注解是以“@注释名”在代码中存在的,还可以添加一些参数值,例如:
@SuppressWarnings(value=”unchecked”)
@Override
@Deprecated
@Override
- 只能标记在重写的方法上。用于检测被标记的方法为有效的重写方法,如果不是,则报编译错误!
- 注意:加不加
@Override
注解,只是影响编译器对待该重写方法的态度,并不影响该方法是否重写 本质。
@Deprecated
-
用于表示被标记的数据已经过时。可以用于修饰 属性、方法、构造、类、包、局部变量、参数。
-
它会被编译器程序读取。编译器读取到程序员使用了
@Deprecated
标记过的类、方法等时,就会弹出警告。
@SuppressWarnings
-
抑制编译警告。可以用于修饰类、属性、方法、构造、局部变量、参数
-
它会被编译器程序读取。编译器读取到它抑制的警告,则会消除警告。
四种元注解
元注解是用于修饰其他注解的注解
@Retention
:指定注解的作用范围,三种:SOURCE
、CLASS
、RUNTIME
@TARGET
:指定注解在哪可以使用@Documented
:指定该注解是否会在javadoc
中体现@Inherited
:子类会继承父类注解