一、抽象类
1.抽象类的概念
如果一个类不适合用来创建一个具体的对象,这个类就可以设置成抽象类。
比如我们经常使用的Animal类,它不适合实例化一个对象,我们通常定义一个具体的类比如Dog类继承Animal类,再来实例化对象。
2.抽象类的语法
abstract class Animal{
//抽象类也可以有构造方法
public Animal(int age, String name) {
this.age = age;
this.name = name;
}
int age;
String name;
abstract void show();//抽象方法,可以没有方法体
}
注意:抽象类也是类,除了不能用来实例化一个对象、可以含有抽象方法,其余都和普通类一样。
!!!虽然抽象类不能实例化一个对象,但是它可以被向上转型。
public class Test{
//向上转型
public static void animalTest(Animal animal){
animal.show();
}
public static void main(String []args){
Animal animal=new Dog(1,"小狗");//向上转型
Animal animal1=new Dog(1,"小猫");//向上转型
animalTest(animal);
animalTest(animal1);
}
}
abstract class Animal{
public Animal(int age, String name) {
this.age = age;
this.name = name;
}
int age;
String name;
abstract void show();
}
class Dog extends Animal{
public Dog(int age, String name) {
super(age, name);
}
void show(){
System.out.println("汪汪叫");
}
}
class Cat extends Animal{
public Cat(int age, String name) {
super(age, name);
}
void show(){
System.out.println("喵喵叫");
}
}
3.抽象类的特性
1.抽象类不能直接实例化对象
2.抽象方法不能被private修饰
3.抽象方法不能被final和static修饰,因为抽象方法要被子类重写
4.抽象类必须被继承,并且继承后子类要重写父类中的抽象方法,否则子类也是抽象类,必须要使用 abstract 修饰
5.抽象类中不一定包含抽象方法,但是有抽象方法的类一定是抽象类
6.抽象类中可以有构造方法,供子类创建对象时,初始化父类的成员变量
4.抽象类的作用
二、接口
1.接口的概念
Java中的接口与我们生活中的接口有类似的地方,比如USB接口,每个人的电脑都有USB接口,不论是谁的接口都可以插上同一个U盘,在Java中,接口可以看成是:多个类的公共规范,是一种引用数据类型。
2.接口的语法
public interface Ifly {
public abstract void fly();
void run();//每个方法默认由public abstract修饰,因此可以不写
public static final int a=5;
int b=10;//每个成员变量默认由public static final 修饰,也可以不写
}
3.接口的使用
public class 类名称 implements 接口名称{
//......
}
//实现Iable接口
class Cat extends Animal implements Iable{
public Cat(int age, String name) {
super(age, name);
}
void show(){
System.out.println("喵喵叫");
}
//实现接口的方法是,它的访问权限修饰符必须为public!!!
public void able(){
System.out.println("虽然是狗,但它有飞行能力");
}
}
interface Iable{
void able();
}
4.接口的特性
public class TestUSB {
public static void main(String[] args) {
USB usb = new USB();
}
}
// Error:(10, 19) java: day20210915.USB是抽象的; 无法实例化
2.接口中每一个方法都是public的抽象方法, 即接口中的方法会被隐式的指定为 public abstract(使用其它修饰符会报错)
public interface USB {
// Error:(4, 18) java: 此处不允许使用修饰符private
private void openDevice();
void closeDevice();
}
3.接口中的方法是不能在接口中实现的(default,static修饰的方法除外),只能由实现接口的类来实现
public interface USB {
void openDevice();
// 编译失败:因为接口中的方式默认为抽象方法
// Error:(5, 23) java: 接口抽象方法不能带有主体
void closeDevice(){
System.out.println("关闭USB设备");
}
static void fly(){//static修饰的方法可以由有方法体
System.out.println("起飞了");
}
default void run(){//default修饰的方法也可以有方法体
System.out.println("跑起来");
}
}
4.重写接口中方法时,不能使用默认的访问权限
public interface USB {
void openDevice(); // 默认是public的
void closeDevice(); // 默认是public的
}
public class Mouse implements USB {
@Override
void openDevice() {
System.out.println("打开鼠标");
}
// ...
}
// 编译报错,重写USB中openDevice方法时,不能使用默认修饰符
// 正在尝试分配更低的访问权限; 以前为public
5.接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量
public interface USB {
double brand = 3.0; // 默认被:final public static修饰
void openDevice();
void closeDevice();
}
public class TestUSB {
public static void main(String[] args) {
System.out.println(USB.brand); // 可以直接通过接口名访问,说明是静态的
// 编译报错:Error:(12, 12) java: 无法为最终变量brand分配值
USB.brand = 2.0;// 说明brand具有final属性
}
}
6.接口中不能有静态代码块和构造方法
public interface USB {
// 编译失败
public USB(){
}
{} // 编译失败
void openDevice();
void closeDevice();
}
public class Test{
//接口的向上转型
public static void testIable(Iable iable){
iable.able();//注意:这里与普通多态一样,只能调用接口中有的方法
}
public static void main(String []args){
Iable iable=new Dog(1,"小狗");
Iable iable1=new Cat(1,"小猫");
testIable(iable);
testIable(iable1);
}
}
class Animal{
public Animal(int age, String name) {
this.age = age;
this.name = name;
}
int age;
String name;
}
class Dog extends Animal implements Iable{
public Dog(int age, String name) {
super(age, name);
}
public void able(){//重写接口的方法
System.out.println("虽然是猫,但是它会狗叫");
}
}
class Cat extends Animal implements Iable{
public Cat(int age, String name) {
super(age, name);
}
public void able(){//重写接口的方法
System.out.println("虽然是狗,但它有飞行能力");
}
}
interface Iable{
void able();
}
//运行结果
//虽然是猫,但是它会狗叫
//虽然是狗,但它有飞行能力
5.实现多个接口
class Animal{
public Animal(int age, String name) {
this.age = age;
this.name = name;
}
int age;
String name;
}
再创建一些接口,分别表示“会飞的”、“会跑的”、“会游泳的”。
interface IRunning{
void running();
}
interface ISwimming{
void swimming();
}
interface IFlying{
void fly();
}
再创建鸟类和狗类继承Animal并实现相应能力的接口:
class Dog extends Animal implements IRunning,ISwimming{
@Override
public void running() {
System.out.println(this.name+"会跑");
}
@Override
public void swimming() {
System.out.println(this.name+"会游泳");
}
public Dog(int age, String name) {
super(age, name);
}
public void able(){
System.out.println("虽然是猫,但是它会狗叫");
}
}
class Brid extends Animal implements IFlying{
public void fly() {
System.out.println(this.name+"会飞");
}
public Brid(int age, String name) {
super(age, name);
}
public void able(){
System.out.println("鸟会飞");
}
}
最后,在main方法里进行使用
public static void run(IRunning iRunning){
iRunning.running();
}
public static void swim(ISwimming iSwimming){
iSwimming.swimming();
}
public static void fly(IFlying iFlying){
iFlying.fly();
}
public static void main(String []args){
IRunning iRunning=new Dog(1,"狗");
iRunning.running();
ISwimming iSwimming=new Dog(1,"狗");
iSwimming.swimming();
IFlying iFlying=new Brid(2,"鸟");
iFlying.fly();
}
6.接口间的继承
interface IRunning1{
void running();
}
interface ISwimming1{
void swimming();
}
//接口之间的继承
interface IAble extends IRunning1,ISwimming1{
void able();
}
//那么以后在类中实现Iable这个接口时,就必须重写running,swimming,able三个方法
7.接口使用实例
8.Clonable接口和深拷贝
class Animal implements Cloneable {
private String name;
@Override
public Animal clone() {
Animal o = null;
try {
o = (Animal)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return o;
}
}
public class Test {
public static void main(String[] args) {
Animal animal = new Animal();
Animal animal2 = animal.clone();
System.out.println(animal == animal2);
}
}
// 输出结果
// false
class Money {
public double m = 99.99;
}
class Person implements Cloneable{
public Money money = new Money();
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class TestDemo3 {
public static void main(String[] args) throws CloneNotSupportedException {
Person person1 = new Person();
Person person2 = (Person) person.clone();
System.out.println("通过person2修改前的结果");
System.out.println(person1.money.m);
System.out.println(person2.money.m);
person2.money.m = 13.6;
System.out.println("通过person2修改后的结果");
System.out.println(person1.money.m);
System.out.println(person2.money.m);
}
}
// 执行结果
通过person2修改前的结果
99.99
99.99
通过person2修改后的结果
13.6
13.6
9.抽象类和接口的区别
10.Object类
10.1.获取对象信息
// Object类中的toString()方法实现:
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
10.2.比较equals方法
public class Test{
public static void main(String []args){
Animal animal1=new Animal(1,"动物");
Animal animal2=new Animal(1,"动物");
System.out.println(animal2==animal1);
System.out.println(animal2.equals(animal1));
}
}
class Animal{
public Animal(int age, String name) {
this.age = age;
this.name = name;
}
int age;
String name;
}
//打印结果
//false
//false
我们看看equals方法的实现
可以看到,equals方法依然比较的是地址,因此我们需要对equals方法进行重写
public class Test{
public static void main(String []args){
Animal animal1=new Animal(1,"动物");
Animal animal2=new Animal(1,"动物");
System.out.println(animal2==animal1);
System.out.println(animal2.equals(animal1));
}
}
class Animal{
public Animal(int age, String name) {
this.age = age;
this.name = name;
}
//对equals方法进行重写
public boolean equals(Object obj) {
Animal tmp=(Animal)obj;
return tmp.name.equals(this.name) && tmp.age==this.age;
}
int age;
String name;
}
//打印结果
//false
//true
10.3.hashCode方法
class Person {
public String name;
public int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
public class TestDemo4 {
public static void main(String[] args) {
Person per1 = new Person("gaobo", 20) ;
Person per2 = new Person("gaobo", 20) ;
System.out.println(per1.hashCode());
System.out.println(per2.hashCode());
}
}
//执行结果
460141958
1163157884
class Person {
public String name;
public int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
public class TestDemo4 {
public static void main(String[] args) {
Person per1 = new Person("gaobo", 20) ;
Person per2 = new Person("gaobo", 20) ;
System.out.println(per1.hashCode());
System.out.println(per2.hashCode());
}
}
//执行结果
460141958
460141958
三、内部类
public class OutClass {
class InnerClass{
}
}
// OutClass是外部类
// InnerClass是内部类
注意:
内部类的分类
public class OutClass {
// 成员位置定义:未被static修饰 --->实例内部类
public class InnerClass1{
}
// 成员位置定义:被static修饰 ---> 静态内部类
static class InnerClass2{
}
public void method(){
// 方法中也可以定义内部类 ---> 局部内部类:几乎不用
class InnerClass5{
}
}
}
1.静态内部类
被static修饰的内部成员类称为静态内部类。
public class OutClass {
private int a;
static int b;
public void methodA() {
a = 10;
System.out.println(a);
}
public static void methodB() {
System.out.println(b);
}
// 静态内部类:被static修饰的成员内部类
static class InnerClass {
public void methodInner() {
// 在内部类中只能访问外部类的静态成员
// a = 100; // 编译失败,因为a不是类成员变量
b = 200;
// methodA(); // 编译失败,因为methodB()不是类成员方法
methodB();
}
}
public static void main(String[] args) {
// 静态内部类对象创建 & 成员访问
OutClass.InnerClass innerClass = new OutClass.InnerClass();
innerClass.methodInner();
}
}
2.实例内部类
public class OutClass {
private int a;
static int b;
int c;
public void methodA() {
a = 10;
System.out.println(a);
}
public static void methodB() {
System.out.println(b);
}
// 实例内部类:未被static修饰
class InnerClass {
int c;
public void methodInner() {
// 在实例内部类中可以直接访问外部类中:任意访问限定符修饰的成员
a = 100;
b = 200;
methodA();
methodB();
// 如果外部类和实例内部类中具有相同名称成员时,优先访问的是内部类自己的
c = 300;
System.out.println(c);
// 如果要访问外部类同名成员时候,必须:外部类名称.this.同名成员名字
OutClass.this.c = 400;
System.out.println(OutClass.this.c);
}
}
public static void main(String[] args) {
// 外部类:对象创建 以及 成员访问
OutClass outClass = new OutClass();
System.out.println(outClass.a);
System.out.println(OutClass.b);
System.out.println(outClass.c);
outClass.methodA();
outClass.methodB();
System.out.println("=============实例内部类的访问=============");
// 要访问实例内部类中成员,必须要创建实例内部类的对象
// 而普通内部类定义与外部类成员定义位置相同,因此创建实例内部类对象时必须借助外部类
// 创建实例内部类对象
OutClass.InnerClass innerClass1 = new OutClass().new InnerClass();
// 上述语法比较怪异,也可以先将外部类对象先创建出来,然后再创建实例内部类对象
OutClass.InnerClass innerClass2 = outClass.new InnerClass();
innerClass2.methodInner();
}
}
3.匿名内部类
//普通类的匿名内部类
class Out{
public void test1(){
System.out.println("haha");
}
}
public class Test{
public class void main(String[]args){
new Out(){
//也可以对test1重写
}.test1();
}
}
//通过接口实现内部类
interface IA{
void test1();
}
public class Test{
public static void main(String[]args){
//这个类实现了IA接口,并且重写了test1方法
IA a=new IA(){
public void test1(){
System.out.println("haha");
}
};
a.test1();
}
}
4.局部内部类
public class OutClass {
int a = 10;
public void method() {
int b = 10;
// 局部内部类:定义在方法体内部
// 不能被public、static等访问限定符修饰
class InnerClass {
public void methodInnerClass() {
System.out.println(a);
System.out.println(b);
}
}
// 只能在该方法体内部使用,其他位置都不能用
InnerClass innerClass = new InnerClass();
innerClass.methodInnerClass();
}
public static void main(String[] args) {
// OutClass.InnerClass innerClass = null; 编译失败
}
}