1.抽象类
在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。
1.1抽象类
抽象类在class前用abstract修饰,抽象类不能被实例化
public abstract class Animal(){
}
也有人把抽象称为Java面向对象的第四大特征,使用抽象类就是为了方便其他类去继承,实现多态
1.2抽象方法
抽象方法在返回值前用abstract进行修饰,抽象方法没有方法体,形如
public abstract void rap(){}
继承抽象类就要重写抽象类中的抽象方法
1.3抽象类和抽象方法的联系
抽象类中可以有抽象方法也可以有非抽象方法,除了不能实例化以外和普通的类一样
抽象类的子类必须重写抽象类中所有的抽象方法,除非子类也是一个抽象类
有抽象方法的类一定是抽象类,但抽象类不一定有抽象方法
2. 接口
接口就是一种规范, 如 USB 接口,Type-C接口,
通过接口,我们可以指定一些规范,如可以规定方法名,返回值类型,方法的形参等等
2.1接口的定义
接口使用关键字interface
来定义
public interface 接口名{
//定义抽象方法
void method();
}
接口就是定义了一些规范,需要其他类去实现这些规范,所以接口只能是public的,接口中的方法也都是默认是public abstract 的
public interface Animal{
void sing();//虽然没写为public abstract void sing();
void jump();
void rap();
void basketball();
}
2.2实现接口
接口只是定义了一些规范,需要类实现接口,来重写接口中的各种抽象方法
实现接口需要用到implement
关键字,格式如下
public class 类名 implement 接口名1,接口名2...,接口名n{}
不同于继承,一个类可以实现多个接口,多个接口名之间用逗号隔开
实现接口必须要实现接口中的所有抽象方法
public class Chicken implements Animal {
String name = "菜徐琨";
@Override
public void sing() {
System.out.println(this.name + "在唱");
}
@Override
public void jump() {
System.out.println(this.name + "在跳");
}
@Override
public void rap() {
System.out.println(this.name + "在rap");
}
@Override
public void basketball() {
System.out.println(this.name + "在打篮球");
}
}
2.3 接口中的成员
2.3.1常量和抽象方法
在 jdk7
及以前版本,接口中只能有常量和抽象方法
public interface True {
String NAME = "丁真";
void smoke();
}
接口中定义的成员变量默认是public static final
,即我们只能在接口中定义一个常量
2.3.2默认方法
jdk8
中,允许我们在接口中定义默认方法.
默认方法格式如下
public interface True {
default void listen(){
//方法体
System.out.println("芝士雪豹");
}
}
默认方法用关键字default
修饰,允许方法有方法体
类在实现接口时,可以不重写默认方法
注意:
如果两个接口中有相同的默认方法,一个类同时实现了这两个接口,则必须要重写默认方法
在接口升级的时候如果某些方法不想让所有实现类进行重写,可以把方法定义为默认方法
2.3.3 静态方法
在jdk8
中,我们可以去定义静态方法,静态方法有方法体且不能被重写
public interface True {
static void smoke(){
System.out.println("我是电子香烟的代表者丁真");
}
}
接口静态方法的使用与类中静态方法的使用类似
通过接口名.方法名(实参)来进行调用
public class Test {
public static void main(String[] args) {
True.smoke();
}
}
2.4 继承与实现的关系
在之前学习继承我们已经了解到,Java类只能单继承,但一个Java类可以实现多个接口,通过这种方法可以做到与C++类似的多继承
Java中接口之间也可以进行继承,通过关键字extends
public interface Animal{
void sing();
void jump();
void rap();
void basketball();
}
public interface Chicken extends Animal{}
接口继承另一个接口后,可以不重写其中的抽象方法
2.5 抽象类与接口的区别和联系
区别:
- 抽象类中的方法基本都是抽象的,但抽象类中可以存在非抽象方法.
- 抽象类中可以定义成员变量,但接口中只能定义常量
- 一个类只能继承一个抽象类,但是可以实现多个接口
- 抽象类中可以有构造方法,接口中没有
联系:
- 都可以含有抽象方法
- 都不能被实例化
- 都是为了多态的使用,为了被其他类继承实现
3.代码块
在之前我们已经了解到类的组成包括成员变量,成员方法,构造方法
代码块也是类的一个组成部分,在Java类中用{}包裹的代码就称为代码块
代码块按照位置和声明的不同,可以分为局部代码块,静态代码块,实例代码块和同步代码块
3.1 局部代码块
局部代码块写在方法中,用{}包裹,如下
public static void main(String[] args) {
//一个局部代码块
{
String name = "雪豹";
System.out.println("芝士"+name);//输出芝士雪豹
}
System.out.println("芝士"+name);//无法访问到name
}
作用:
局部代码块可以用于限定变量的生命周期,让变量尽早释放,从而提高内存的利用率
3.2 静态代码块
静态代码块写在类中,用static修饰,如下
public class Demo02 {
public static void main(String[] args) {
System.out.println("王源喜欢传统香烟");
}
static {
System.out.println("丁真喜欢电子香烟");
}
//输出:
//丁真喜欢电子香烟
//王源喜欢传统香烟
}
之前我们了解到,被 static 修饰表示是静态的,
静态代码块也是一样,在类加载时进行,与类一起进行加载,在执行类和加载类时都会执行一次静态代码块
而且静态代码块是自动触发的,在程序启动时静态代码块就会执行一次
作用:
由于静态代码块在类加载和类执行时就会执行的这种特性,一般用于初始化静态资源
3.3 实例代码块(构造代码块)
实例代码块写在类中,如下
public class Demo {
public String name;
{
name = "老八";
System.out.println("实例代码块被执行");
}
public Demo(String name) {
System.out.println("有参构造方法被调用");
this.name = name;
}
public Demo() {
System.out.println("无参构造方法被调用");
}
}
public class Test {
public static void main(String[] args) {
Demo demo1 = new Demo();
Demo demo2 = new Demo("李易峰");
System.out.println(demo1.name + "在吃奥利给");
System.out.println(demo2.name + "在pc");
}
//实例代码块被执行
//无参构造方法被调用
//实例代码块被执行
//有参构造方法被调用
//老八在吃奥利给
//李易峰在pc
}
实例代码块在创建对象时执行,每次调用构造方法来初始化对象,实例代码块都会执行一次
通过上述代码我们也可以知道,实例代码块的执行在构造方法之前
3.4 静态代码块,实例代码块,构造方法的执行顺序
//父类Person
public class Person {
public Person() {
System.out.println("Person构造器被调用");
}
static {
System.out.println("Person静态代码块被调用");
}
{
System.out.println("Person实例代码块被调用");
}
}
//子类Men
public class Men extends Person{
public Men() {
System.out.println("Men构造方法被调用");
}
static{
System.out.println("Men静态代码块被调用");
}
{
System.out.println("Men实例代码块被调用");
}
}
//测试执行顺序
public class Test05 {
public static void main(String[] args) {
Men men = new Men();
}
//Person静态代码块被调用
//Men静态代码块被调用
//Person实例代码块被调用
//Person构造器被调用
//Men实例代码块被调用
//Men构造方法被调用
}
通过上述代码我们可以知道执行顺序是
父类静态代码块 -> 子类静态代码块 ->父类实例代码块->父类构造方法->子类实例代码块->子类构造方法
4 内部类
定义在类内部的类被称为内部类.内部类可分为局部内部类,成员内部类,静态内部类,匿名内部类
4.1 局部内部类
局部内部类就是定义在方法中的类
public class Men {
String name = "丁真";
public void smoke() {
class True {//局部内部类
public void smoke() {
System.out.println(name + "喜欢抽瑞克五代");
}
}
}
}
对象创建需要在方法内部进行,如下
public class Men {
String name = "丁真";
public void smoke() {
class Inner {
public void smoke() {
System.out.println(name + "喜欢抽瑞克五代");
}
}
Inner inner = new Inner();
inner.smoke();
}
}
public class Test {
public static void main(String[] args) {
Men men = new Men();
men.smoke();
}
}
如果想要定义一个在方法中临时使用的类就可以用局部内部类,局部内部类的作用范围只在方法内
4.2 成员内部类
定义类中的非static
的内部类称为成员内部类
public class Phone {
public class HaWei{
public String des = "华为13proMax远峰蓝1TB";
}
}
4.2.1 成员内部类对象的创建
在类内部和之前学习的创建对象方法一致
public class Phone {
public class HaWei{
public String des = "华为13ProMax 1TB 远峰蓝";
public HaWei(String des) {
this.des = des;
}
public HaWei() {
}
}
public HaWei haWei = new HaWei();
}
在类外部创建对象有些繁琐
public class Test05 {
public static void main(String[] args) {
Phone phone = new Phone();
Phone.HaWei haWei1 = phone.new HaWei("华为14ProMax 1TB 暗夜紫");
Phone.HaWei haWei2 = new Phone().new HaWei();//简化写法
System.out.println(haWei1.des);
System.out.println(haWei2.des);
}
}
可能看起来这种写法有些怪,但是仔细思考
成员内部类属于类的一个成员,所以需要创建对象来使用,
而成员内部类也需要创建对象来访问其中的成员变量和成员方法
4.2.2 成员内部类的特点
-
成员内部类中不能定义静态成员,
-
因为成员内部类是非静态的,在非静态中不能使用静态的
在成员内部类中可以使用类中的变量和方法,即使是私有的
public class Phone {
private double price = 12999.9;
String name = "华为14ProMax 1TB 暗夜紫";
public class HaWei {
public void price() {
System.out.println("\"" + name + "\"" + "价格是" + price);
}
}
}
public class Test {
public static void main(String[] args) {
Phone.HaWei haWei = new Phone().new HaWei();
haWei.price();//"华为14ProMax 1TB 暗夜紫"价格是12999.9
}
}
- 如果外部类和内部类的变量重名,可以使用外部类名.this.成员变量来访问
public class Phone {
private double price = 12999.9;
String name = "华为14ProMax 1TB 暗夜紫";
public class HaWei {
String name = "华为13ProMax 1TB 远峰蓝";
public void price() {
System.out.println("\"" + Phone.this.name + "\"" + "价格是" + price);
}
}
}
4.3 静态内部类
在类中用static修饰的内部类称为静态内部类
public class Person {
public static class White{
public static String name = "土块";
public String des = "十七张牌你能秒我?"
public static void say(){
System.out.println(name +"给阿姨倒一了杯卡布奇诺");
}
public void sit(){
System.out.println("全体起立");
}
}
}
public class Test {
public static void main(String[] args) {
Person.White.say();
Person.White white = new Person.White();
white.sit();
//土块给阿姨倒一了杯卡布奇诺
//全体起立
}
}
静态内部类中可以定义静态成员变量和普通成员变量,也可以定义静态方法和成员方法
在使用时,静态变量和静态方法无需新建对象,成员变量和成员方法的使用需要新建对象
4.4 匿名内部类
匿名内部类就是没有名字的内部类,实质上是一个对象,格式如下
new 接口名/类名(){
//重写要重写的方法
};
当我们需要创建一个类或接口的子类对象,但是这个子类只需使用一次时,
就可以考虑使用匿名内部类,而不是单独创建一个类.
//定义一个接口
public interface Star {
void pc();
}
public class Test {
public static void main(String[] args) {
Star star = new Star() {
@Override
public void pc() {
System.out.println("加藤峰在pc");
}
};
star.pc();
}
}