介绍:除了前面介绍的关于类、对象的基本语法之外,下面继续介绍Java面向对象的特性。
一、 包装类:
从JDK 1.5以后,Java就提供了自动装箱和自动拆箱操作,即:
- 自动装箱:将一个基本类型的变量直接赋给对应的包装类变量;
- 自动拆箱:允许一个包装类对象直接赋给一个对应的基本类型。
1. 基本数据类型==>String类型
- 利用String类的valueOf()方法
- 与""进行+运算
2. String类型==>基本数据类型
- 利用相应包装类提供的parseXxx(String)静态方法;
- 通过包装类的构造器:new Xxx(String)
二、 处理对象
Java对象都是Object类的实例,都可以直接调用该类中定义的方法,这些方法提供了处理Java对象的通用方法。
1. toString方法:
Object类提供的toString方法总是返回该对象实现类的“类名 + @ + hashCode”值,如果用户需要自定义类能实现“自我描述”的功能,就必须重写Object类的toString方法。
class Apple{
private String color;
private double price; public Apple(String color, double price) {
this.color = color;
this.price = price;
} public Apple() { } public String toString() {
return "这个苹果颜色是:" + this.color + "色, " + this.price + "元。";
}
} public class ToStringTest {
public static void main(String[] args) {
Apple a = new Apple("红",10);
System.out.println(a.toString());//这个苹果颜色是:红色, 10.0元。
}
}
2. ==和equals方法:
1) ==:
- 当时用==来判断两个变量是否相等时,如果两个变量都是基本类型的变量,且都是数值类型(不一定要求数据类型严格相同),则只要两个变量的值相等,就返回true;
- 对于两个引用类型变量,只有它们指向同一个对象时,==判断才会返回true。
String s1 = new String("hello");
String s2 = new String("hello");
System.out.println(s1 == s2);//false
String s3 = "hello";
String s4 = "he";
String s5 = "llo";
String s6 = s4 + s5;
//s6后面的字符串不能在编译时就确定下来,不能引用常量池中的字符串
System.out.println(s3 == s6);//false
2) equals:很多时候,在判断两个引用变量是否相等时,我们希望有一种“值相等”的判断规则,此时需要重写Object类所提供的equals方法,如果不重写的话这个方法与使用==没有区别。
【注意】String已经重写了equals方法,只要两个字符串包含的字符序列相同,则通过equals比较将返回true。
3. 单例类(Singleton):该类始终只能创建一个实例
- 使用一个private类变量来缓存曾经创建的实例
- 将构造器私有化
- 提供一个类方法,用于返回这唯一的实例
public class SingletonTest {
private static SingletonTest instance; private SingletonTest() { } public SingletonTest getInstance() {
if(instance == null)
instance = new SingletonTest();
return instance;
}
}
三、 final修饰符:可用于修饰类、变量和方法
1. final成员变量:final修饰的成员变量能指定初始值的位置如下:
- 类变量:静态初始化块中或声明该变量时指定初始值(二选一);
- 实例变量:非静态初始化块、声明该变量处或构造器中指定初始化值(三选一)。
2. final局部变量:系统不会为final局部变量进行初始化,因此必须显式地初始化。
【注意】 final修饰引用类型变量时,final只保证这个引用类型变量所引用的地址不会发生变化,但是这个对象的属性值可以改变。
3. 可执行“宏替换”的final变量:
当final变量被直接赋值或被赋的表达式只是基本的算术表达式或字符串连接运算,并且没有访问普通变量、调用方法时,Java编译器会将这种final变量当成“宏变量”处理。
final String str1 = "hello " + 100;
final String str2 = "hello " + String.valueOf(100);
System.out.println(str1 == "hello 100");//true
System.out.println(str2 == "hello 100");//false
str1在编译时就能确定变量的值,因此被当做“宏变量”,但是str2的值需要调用String类的方法,因此在编译时无法确定str2的值,进而不会被当做“宏变量”处理。
4. final方法:final修饰的方法不可以被重写,但是对于一个private方法,即使使用final对其进行修饰,在子类中仍然可以定义一个与之相同名称、形参的方法(此时不能被称为重写,而是定义了一个新方法)。
5. final类:final修饰的类不能拥有子类。
四、 抽象类:
抽象方法和抽象类的定义:
- 必须使用abstract来修饰,并且抽象方法没有方法体;
- 抽象类不能被实例化;
- 含有抽象方法的类(包括:直接定义了一个抽象方法、继承了一个抽象类但是没有完全实现父类的抽象方法、实现了一个接口但是没有完全实现接口的抽象方法)只能被定义成抽象类。
五、 接口:
Java 8对接口进行了改进,允许在接口中定义默认(default)方法。
1. 接口的定义: 接口是一种规范,使用interface来定义,其地位与类一致,但是没有构造器,因此不能实例化。一般通过类来实现(implements)接口中的抽象方法,而且一个类可以实现(implements)多个接口,从而打破了单继承的局限性。另外,接口与接口之间不仅可以继承,还可以多继承(因为接口没有构造器,因此不会出现super的错误)。
JDK 7及以前:只能定义全局常量和抽象方法
- 全局常量:public static final(可以省略不写,因为接口中默认就是这个)
- 抽象方法:public abstract
JDK 8:在以前的基础上可以定义静态方法、默认方法
- 接口中的静态方法只能通过 接口.静态方法 来使用
- 默认方法可以在实现类中重写
- 接口和父类的方法同名时,执行的是父类的方法
- 不允许同一个实现类的两个接口有同名的默认方法(接口冲突),此时需要在实现类中重写此方法
//*****************************JAVA 7**************************
interface Attackable{
void attack();
} interface Flyable{
//无法定义构造器 //全局变量:
public static final int MAX_SPEED = 7900;
int MIN_SPEED = 1;//public static final可以省略 //抽象方法:
public abstract void fly();
void stop();//public abstract 可以省略
} class Plane implements Flyable{
//通过类来实现(implements)接口中的抽象方法
@Override
public void fly() {
System.out.println("The plane is flying...");
} @Override
public void stop() {
System.out.println("Stop the plane...");
}
} class Bullet extends Object implements Flyable,Attackable{
//先写继承的父类,后写实现的具体接口
@Override
public void attack() {
System.out.println("Using bullet");
}
@Override
public void fly() { }
@Override
public void stop() { }
}
//接口与接口之间不仅可以继承,还可以多继承
interface A{}
interface B{}
interface C extends A,B{}
//*****************************JAVA 8新特性**************************
interface CompareA{
//静态方法
static void method1() {
System.out.println("CompareA:北京!");
} //默认方法
default void method2(){
System.out.println("CompareA:上海!");
} default void method3(){
System.out.println("CompareA:广东!");
}
} interface CompareB{
default void method3(){
System.out.println("CompareB:广东!");
}
} class Sup {
public void method2(){
System.out.println("Supclass2:...");
}
public void method3(){
System.out.println("Supclass3:...");
}
} class Sub extends Sup implements CompareA,CompareB{
public void method3(){ //不允许同一个实现类的两个接口有同名的默认方法(接口冲突),
// 此时需要在实现类中重写此方法 System.out.println("OverWrite");
} public void myMethod(){
method3();//调用自己重写的方法
super.method3();//调用父类的方法
CompareA.super.method3();//调用接口中的方法
CompareB.super.method3();//调用接口中的方法
}
}
//接口的使用案例
public class USBTest {
public static void main(String[] args) {
Computer computer = new Computer(); //1.非匿名实现类Flash的非匿名对象flash
Flash flash = new Flash();
computer.transferData(flash); //2.非匿名实现类Flash的匿名对象
computer.transferData(new Printer()); //3.匿名实现类的非匿名对象phone
USB phone = new USB() {
@Override
public void start() {
System.out.println("启动手机");
} @Override
public void stop() {
System.out.println("关闭手机");
}
};
computer.transferData(phone); //4.匿名实现类的匿名对象
computer.transferData(new USB(){//匿名实现类
@Override
public void start() {
System.out.println("mp3开始工作");
} @Override
public void stop() {
System.out.println("mp3结束工作");
}
});
}
} class Computer{
public void transferData(USB usb){
usb.start();
System.out.println("传输细节");
usb.stop();
}
} interface USB{
void start();
void stop();
} class Flash implements USB{
@Override
public void start() {
System.out.println("U盘开始工作");
} @Override
public void stop() {
System.out.println("U盘停止工作");
}
} class Printer implements USB{
@Override
public void start() {
System.out.println("打印机开始工作");
} @Override
public void stop() {
System.out.println("打印机停止工作");
}
}
2. 简单工厂模式和命令模式:(待补充)
六、 内部类:
在Java中,允许一个类的定义位于另一个类的内部,前者称为内部类,后者称为外部类。
- 成员内部类:静态成员内部类、非静态成员内部类
- 作为一个类:
类内可以定义属性、方法、构造器等
可以用final、abstract修饰
- 作为外部类的成员:
能够调用外部类的内容
可以用static修饰(外部类不能用static修饰)
可以用4种不同的权限修饰
- 局部内部类:位于方法体、代码块、构造器
public class InnerClassTest {
public static void main(String[] args) {
//1.1 静态成员内部类实例化
Person.InnerClassA demo1 = new Person.InnerClassA();
demo1.play(); //1.2 非静态成员内部类实例化
Person demo2 = new Person();
Person.InnerClassB demo22 = demo2.new InnerClassB();
demo22.sing();
demo22.display("display"); //2.局部内部类
}
} class Person{
String name = "Person";
int age; public void eat(){
System.out.println("eating...");
} //1.1 静态成员内部类
static class InnerClassA{
int age;
public InnerClassA(){ }
public void play(){
System.out.println("静态成员内部类");
}
} //1.2非静态成员内部类
class InnerClassB{
String name = "InnerClassB";
public InnerClassB(){ }
public void sing(){
System.out.println("非静态成员内部类");
eat();//是Person.this.eat()的略写,调用外部类的方法
}
public void display(String name){
System.out.println(name);//形参
System.out.println(this.name);//内部类的name
System.out.println(Person.this.name);//外部类的name
}
} //2.1 方法内的局部内部类
public void method(){
final int num = 10; //在局部内部类调用外部内方法中的局部变量时,
//前提是该局部变量必须是final的,jdk8后final可以省略不写 class InnerClass1{ }
} //2.2 代码块内的局部内部类
{
class InnerClass2{ }
} //2.3 构造器内的局部内部类
public Person(){
class InnerClass3{ }
} }