day02【接口、final关键字、代码块、权限修饰符、枚举】

day02【接口、final关键字、代码块、权限修饰符、枚举】

今日内容

  • 接口(掌握)

  • final关键字(掌握)

  • 枚举(理解)

教学目标

  • 写出定义接口的格式
  • 写出实现接口的格式
  • 说出接口中成员的特点
  • 描述final修饰的类的特点
  • 描述final修饰的方法的特点
  • 能够定义枚举

 

第一章 static关键字

static静态的引入

需求:

1)创建一个Person类,并在Person类中定义两个属性 name和age。

2)在Person类中定义两个函数sleep和speak来描述人的睡觉和说话功能。

3)定义一个测试人的类PersonTest,并在这个类中创建两个Person的对象,使用创建好的对象分别调用sleep()函数。

/*
创建一个Person类
*/
class Person
{
//人的属性
String name;//姓名
int age;//年龄
//人的行为
//睡觉功能
void sleep()
{
System.out.println("睡觉。。。。");
}
//说话工能
void speak()
{
System.out.println("姓名="+this.name+",年龄="+this.age);
}
}

class PersonTest
{
public static void main(String[] args)
{
//创建Person类的对象
Person p1=new Person();
//使用p1对象调用Person类中的sleep函数
p1.sleep();
Person p2=new Person();
//使用p2对象调用Person类中的sleep函数
p2.sleep();
}
}

在PersonTest类中的main方法中,我们创建了2个Person对象,仅仅只是为了调用sleep方法。而在sleep方法中并没有访问到和当前对象有关的任何成员变量数据。

我们创建对象的时候,对象会在堆中出现,并且所有的成员变量都会随着当前这个对象的创建在堆中存在,并且这些成员变量就来描述当前这个对象的所有属性数据。 

我们能不能在不创建对象的前提下就可以调用sleep函数呢?

由于我们目前所学的技术,要调用某个类中的函数, 必须先创建这个类的对象,然后通过对象调用函数。

如果在程序中,仅仅只是为了调用函数,而函数中并没有访问到这个对象自己的一些数据,那么这时可以不使用对象,同时就不用在程序中创建这个对象。

这时我们可以使用Java中提供的static关键字修饰这个函数,这样就可以直接通过当前函数所在的类名直接调用函数, 而不需要对象。

static关键字: 表示静态的。

static属于一个修饰符号,用来修饰成员变量、成员函数的。

2 概述

关于 static 关键字的使用,它可以用来修饰的成员变量和成员方法,被修饰的成员是属于类的,而不是单单是属于某个对象的。也就是说,既然属于类,就可以不靠创建对象来调用了。可以直接使用类名调用。

3 定义和使用格式

静态方法

注意:当一个函数中没有访问到这个类创建出的对象中的数据(实例/对象成员变量)时,这个函数才能被static修饰:

static修饰函数的格式:

修饰符 返回值类型  函数名( 参数 )

{

函数体;

}

这里的修饰符:就是static

static的使用格式举例:

   修饰成员变量:   static  int  num =100;

   修饰成员函数:   static  void  test(String name){  }

被static关键字修饰的函数,它不需要对象调用,可以直接通过   类名.函数名(实际参数)   方式调用。

调用演示代码如下:

class Student
{
 public static void sleep()
{
System.out.println("睡觉。。。。。。");
}
}
public class StuDemo2 {
   public static void main(String[] args) {      
       // 调用静态方法
       Student.sleep();
  }
}

小结:

    1、 被static修饰的成员函数,称为静态成员函数。

    2、 通常在访问静态函数的方式是通过类名来访问。

3、 访问静态成员函数的方式:  类名.静态函数名(参数列表)。

4、 只要一个函数被static关键字所修饰,那么这个函数就和对象没有关系了,只和类有关系。

5、 在静态函数中不能使用非静态成员变量。

静态函数使用注意事项(重要,难理解)

1、静态函数,也称为类函数。使用类名去调用静态的函数。

     非静态的函数,也称为实例(对象)函数。使用对象名(实例名)来调用实例函数。

2、静态关键字是一个修饰符。可以修饰类中的成员函数和成员变量。不能修饰构造函数

3、静态函数它是在类加载的时候,就在内存中加载完成,可以直接运行的函数。

非静态函数,它的运行必须是在类加载完成之后,通过new关键字创建出对象之后,通过对象才能调用。

4、静态函数中不能调用非静态函数。

因为静态函数在类加载完成之后通过类名可以直接调用,而这时很有可能还没有创建对象。非静态函数必须依赖于对象才能运行。

5、非静态函数中是可以调用静态函数的。

当非静态函数可以运行的时候,在内存中一定有个对象,既然有对象了,就说明对象所属的那个类肯定已经被加载完成了。类都加载完成了,静态函数已经准备就绪。

6、静态函数中不能使用this 关键字。

this关键字它表示的是当前调用这个函数的那个对象。而在静态函数中是没有对象的。

静态函数使用注意事项演示的代码:

/*
创建一个Person类
*/
class Person
{
//人的属性
static String name;//姓名
int age;//年龄
//构造函数
Person(String name,int age)
{
this.name=name;
this.age=age;
}
//人的行为
//睡觉功能
//函数被static修饰了,此时他不需要对象调用,
//可以使用类名直接调用
static void sleep()//静态函数
{
System.out.println("睡觉。。。。"+name);
// System.out.println("this="+this);//报错
//speak();//在静态函数中不可以调用非静态函数
}
//说话工能
void speak()//非静态函数
{
//在非静态函数中可以调用静态函数
sleep();
System.out.println("姓名="+this.name+",年龄="+this.age);
}
}
class PersonTest1
{
public static void main(String[] args)
{
//创建Person类的对象
Person p1=new Person("黑旋风",13);
//使用p1对象调用Person类中的sleep函数
//直接通过类名调用被static修饰的函数
Person.sleep();
//p1.sleep();
//Person p2=new Person();
//使用p2对象调用Person类中的sleep函数
//Person.sleep();
//调用自定义函数
/*int sum =getSum(1,2);
System.out.println("sum="+sum); */
}
/*public int getSum(int a,int b)
{
return a+b;
}*/
}

 

静态函数使用注意事项演示的图解:

 

 

类变量

静态关键字是一个修饰符,它主要用来修饰类中的成员(变量和函数)。

需求:计算圆的面积

步骤和分析:

1.定义一个描述圆的类Circle;

2.在Circle类中定义一个变量r用来保存圆的半径;

3.在Circle类中定义一个变量PI用来保存常量3.14;

4.在Circle类中定义一个构造函数用来给半径r初始化值;

5.在Circle类中自定义函数计算圆的面积;

6.定义一个测试圆的类CircleTest;

代码如下所示:

/*
描述一个圆,计算圆的面积
*/
class Circle
{
//圆的半径
private double r;
//圆周率
private double PI=3.14;
//构造函数
Circle(double r)
{
this.r=r;
}
//对外提供一个计算圆面积的函数
double getArea()
{
return  r*r*PI;
}
}
class CircleTest
{
public static void main(String[] args)
{
//创建圆的对象
Circle circle1=new Circle(4);
//使用对象调用计算圆面积的函数
double area1=circle1.getArea();
System.out.println("area1="+area1);
//创建圆的对象
Circle circle2=new Circle(6);
//使用对象调用计算圆面积的函数
double area2=circle2.getArea();

System.out.println("area2="+area2);
}
}

上述程序有问题:

创建的所有圆对象中都一个相同的属性数据PI,而把这个数据随着对象的创建在堆中保存,对象越多,浪费的内存空间就会越多。

如下图所示:

 

如果一个成员变量的值所有对象都相同,这时我们可以让这个变量在内存中只有一个,然后所有对象共享这个值。

要解决这个问题,就可以通过静态关键字来修饰这个成员变量,当某个成员变量被静态关键字修饰了,这个成员变量就变成所有对象共享的一个变量,并且这个变量所在的内存空间也发生了改变。

这个变量被加载到方法区中。

代码如下所示:

/*
描述一个圆,计算圆的面积
*/
class Circle
{
//圆的半径
private double r;
//圆周率
private static double PI=3.14;
//构造函数
Circle(double r)
{
this.r=r;
}
//对外提供一个计算圆面积的函数
double getArea()
{
return r*r*PI;
}
}

4 静态原理图解(要求图自己会画出来)

需求:

1.定义一个Demo类,在这个类中定义一个实例成员变量x和类变量y,并给y赋值为3;

2.在Demo类中定义一个静态函数print(),打印y的值;

3.在Demo类中定义一个一般函数show,打印x和y的值;

4.定义一个测试类,在测试类中使用类名调用静态函数print(),同时创建对象,并使用对象给对象成员变量x赋值为10,使用创建的对象调用非静态函数;

代码如下:

//静态的内存加载

class Demo
{
int x ;
static int y = 3;

static void print()
{
System.out.println("y="+y);
}

void show()
{
System.out.println("x="+x + ",y=" + y);
}
}

class StaticDemo3
{
public static void main(String[] args)
{
Demo.print();

Demo d = new Demo();

d.x = 10;
d.show();
}
}

JVM在从硬盘上加载一个class文件的时候,这个class文件中的静态成员需要加载到方法区的静态区中。而所有的非静态成员和构造函数,都需要加载到方法区的非静态区中。

说明:

1、jvm虚拟机将class文件从硬盘上加载到方法区中,对于非静态变量,不是开辟内存空间,只是将被翻译成为二进制的代码加载到方法区内存中。

2、jvm虚拟机将class文件从硬盘上加载到方法区中,对于静态变量,jvm虚拟机会在方法区中给静态变量开辟空间,并且先给默认初始化值,然后在显示赋值。

 

static 修饰的内容:

  • 是随着类的加载而加载的,且只加载一次。

  • 存储于一块固定的内存区域(静态区),所以,可以直接被类名调用。

  • 它优先于对象存在,所以,可以被所有对象共享。

 

5 静态代码块(掌握,开发中一定使用)

  • 静态代码块:定义在成员位置,使用static修饰的代码块{ }。

    • 位置:类中方法外。

    • 执行:随着类的加载而执行且执行一次。

格式:

public class ClassName{
  //静态代码块
   static {
    // 执行语句
  }
}

作用:给类变量进行初始化赋值。用法演示,代码如下:

public class Game {
   public static int number;
   public static ArrayList<String> list;

   static {
       // 给类变量赋值
       number = 2;
       list = new ArrayList<String>();
       // 添加元素到集合中
       list.add("张三");
       list.add("李四");
  }
}

6 小结

1.当 static 修饰成员变量或者成员方法时,该变量称为静态变量,该方法称为静态方法。该类的每个对象都共享同一个类的静态变量和静态方法。任何对象都可以更改该静态变量的值或者访问静态方法。但是不推荐这种方式去访问。因为静态变量或者静态方法直接通过类名访问即可,完全没有必要用对象去访问。

2.无static修饰的成员变量或者成员方法,称为实例变量,实例方法,实例变量和实例方法必须创建类的对象,然后通过对象来访问。

3.static修饰的成员属于类,会存储在静态区,是随着类的加载而加载的,且只加载一次,所以只有一份,节省内存。存储于一块固定的内存区域(静态区),所以,可以直接被类名调用。它优先于对象存在,所以,可以被所有对象共享。

4.无static修饰的成员,是属于对象,对象有多少个,他们就会出现多少份。所以必须由对象调用。

5.静态方法中不能使用和对象有关的所有内容:this super....

 

第二章 包和权限修饰符

1 包

包我们每天建的项目就是在一个目录下,我们每次都会建立一个包,这个包在磁盘下其实就是一个目录。包是用来分别类的管理技术,不同的技术类放在不同的包下,方便管理和维护。

在IDEA项目中,建包的操作如下:

包名的命名规范

路径名.路径名.xxx.xxx
// 例如:com.itheima.oa
  • 包名一般是公司域名的倒写。例如:黑马是www.itheima.com,包名就可以定义成com.itheima.技术名称。

  • 包名必须用”.“连接。

  • 包名的每个路径名必须是一个合法的标识符,而且不能是Java的关键字。

2 权限修饰符

在Java中提供了四种访问权限,使用不同的访问权限修饰符修饰时,被修饰的内容会有不同的访问权限,我们之前已经学习过了public 和 private,接下来我们研究一下protected和缺省(default默认)修饰符的作用。

  • public:公共的,所有地方都可以访问。

  • protected:当前类 ,当前包,当前类的子类可以访问。

  • 缺省(没有修饰符):当前类 ,当前包可以访问。

  • private:私有的,当前类可以访问。 public > protected > 缺省 > private

3 不同权限的访问能力

 publicprotected缺省(空的)private
同一类中
同一包中的类  
不同包的子类    
不同包中的无关类      

可见,public具有最大权限。private则是最小权限。

关于受保护权限代码演示如下所示:

代码举例:

需求:

1)定义一个com.fu包,在这个包中创建一个使用public关键字来修饰的Fu类,在这个父类中定义一个使用public修饰的method函数,在这个函数中随便打印一句话;

2)再重新定义一个com.zi包,在这个包中引入com.fu包下面的Fu类,定义一个使用public关键字来修饰的Zi类继承Fu类,在Zi类中定义一个public修饰的函数run,在这个函数中调用父类中method方法;

3)在重新定义一个com.test包,在这个包中引入com.fu包下面的Fu类和com.zi包下面的Zi类,定义一个测试类Test,在这个测试类中分别创建子父类对象,并使用对象分别调用各自中的函数;

父类:

package com.fu;
public class Fu
{
public void method()
{
System.out.println("父类中的方法....");
}
}

子类:

package com.zi;
import com.fu.Fu;//导入Fu类
public class Zi extends Fu
{
public void run()
{
System.out.println("子类中的方法....");
method(); //子类继承了父类 是可以直接去访问父类中的方法
}
}

测试类:

package com.test;
import com.fu.Fu;//导入Fu类
import com.zi.Zi;//导入子类

class Test
{
public static void main(String[] args)
{
Zi z = new Zi();
z.run();//子类对象调用子类中的方法
/*
测试类没有继承Fu这个类,但是可以创建对象,那么就可以调用Fu类中的method方法,
这样导致Fu类中的方法不仅仅子类可以直接去 访问,不是Fu的子类也可以访问了。
*/
Fu f = new Fu();
f.method();
}
}

问题:

com.test包中的Test类没有继承com.fu包中的Fu这个类,但是可以在com.test包中的Test类中创建Fu类对象,那么就可以在com.test包中的Test类中调用com.fu包中的Fu类中的method方法,这样导致Fu类中的方法不仅仅在com.zi包中的子类Zi可以直接去访问,而不是Fu的子类也可以访问了。

上述现象导致Zi类继承Fu类没有任何意义了。

在java中当定义一个类的时候,如果当前这个类中的方法只让自己的子类使用,而其他的类不让使用,这时可以在这个方法前面加上protected关键字。

protected访问权限:专门给子类使用的访问权限。

将上述Fu类中的method函数的修饰符public换成protected。

父类:

package com.fu;
public class Fu
{
protected void method()
{
System.out.println("父类中的方法....");
}
}

子类:

package com.zi;
import com.fu.Fu;//导入Fu类

public class Zi extends Fu
{
public void run()
{
System.out.println("子类中的方法....");
method(); //子类继承了父类 是可以直接去访问父类中的方法
}
}

测试类:

package com.test;
import com.fu.Fu;//导入Fu类
import com.zi.Zi;//导入子类
class Test
{
public static void main(String[] args)
{
Zi z = new Zi();
z.run();//子类对象调用子类中的方法

Fu f = new Fu();
f.method();//报错
}
}

注意:

1)如果父类中的函数使用protected关键字修饰,那么这个函数可以被不同包下的子类访问,而不同包中的其他类不能访问。

2)protected只能修饰成员函数或成员变量,不能修饰类。

3) 四种访问权限,能够跨包访问的只有 public protected(必须是子类)

 

编写代码时,如果没有特殊的考虑,建议这样使用权限:

  • 成员变量使用private ,隐藏细节。

  • 构造方法使用public ,方便创建对象。

  • 成员方法使用public ,方便调用方法。

小贴士:不加权限修饰符,就是默认权限(缺省)

 

第三章 final关键字

1 概述

学习了继承后,我们知道,子类可以在父类的基础上改写父类内容,比如,方法重写。那么我们能不能随意的继承API中提供的类,改写其内容呢?显然这是不合适的。为了避免这种随意改写的情况,Java提供了final 关键字,用于修饰不可改变内容。

  • final: 不可改变。可以用于修饰类、方法和变量。

    • 类:被修饰的类,不能被继承。即final修饰的类不能有子类。

    • 方法:被修饰的方法,不能被子类重写。

    • 变量:被修饰的变量,不能更改变量的值。

2 使用方式

修饰类

被final修饰的类,是最终类,它不能再有子类。这个类不能被继承。

格式如下:

final class 类名 {

}

查询API发现像 public final class Stringpublic final class Mathpublic final class Scanner 等,很多我们学习过的类,都是被final修饰的,目的就是供我们使用,而不让我们所以改变其内容。

修饰方法

格式如下:

修饰符 final 返回值类型 方法名(参数列表){
//方法体
}

被final修饰的函数,这时这个类没有被final修饰,那么这个类是可以有子类的,但是它中被final修饰的方法,在子类中是不能复写的。

修饰变量

  • 局部变量——基本类型

基本类型的局部变量,被final修饰后,只能赋值一次,不能再更改。代码如下:

public class FinalDemo1 {
public static void main(String[] args) {
// 声明变量,使用final修饰
final int a;
// 第一次赋值
a = 10;
// 第二次赋值
a = 20; // 报错,不可重新赋值
}
}

思考,如下两种写法,哪种可以通过编译?

写法1:

final int c = 0;
for (int i = 0; i < 10; i++) {
c = i;
System.out.println(c);
}

写法2:

for (int i = 0; i < 10; i++) {
final int c = i;
System.out.println(c);
}

根据 final 的定义,写法1报错!写法2,为什么通过编译呢?因为每次循环,都是一次新的变量c。这也是大家需要注意的地方。

  • 局部变量——引用类型

引用类型的局部变量,被final修饰后,只能指向一个对象,地址不能再更改。但是不影响对象内部的成员变量值的修改,代码如下:

public class FinalDemo2 {
public static void main(String[] args) {
// 创建 User 对象
final User u = new User();

// 创建 另一个 User对象
// u = new User(); // 报错,指向了新的对象,地址值改变。

// 调用setName方法
u.setName("张三"); // 可以修改
}
}
  • 成员变量

成员变量涉及到初始化的问题,初始化方式有两种,只能二选一:

  1. 显示初始化;

    public class User {
    final String USERNAME = "张三";
    private int age;
    }
  2. 构造方法初始化。

    public class User {
    final String USERNAME ;
    private int age;
    public User(String username, int age) {
    this.USERNAME = username;
    this.age = age;
    }
    }

被final修饰的常量名称,一般都有书写规范,所有字母都大写

小结:final修饰类,类不能被继承。 final修饰方法,方法不能被重写。final修饰变量,变量不能被改值。

 

第四章 代码块

1 构造代码块

  • 构造代码块:

    • 位置:定义在成员位置的代码块{}

    • 执行:每次创建对象都会执行构造代码块

    • 作用:一般是一个类的多个构造方法重复的代码放到构造代码块中

    • 使用场景:

      举例:统计当前类一共创建了几个对象

public class Teacher {
String name;
int age;

//定义计数器
static int count = 0;
//构造代码块
//构造代码块会在构造方法之前执行,并且每次执行构造方法都会先执行构造代码块
{
System.out.println("构造代码块");
//统计
count++;
}

//无参构造
public Teacher(){
System.out.println("构造方法");
}
//有参构造
public Teacher(String name, int age){
this.name = name;
this.age = age;
}
}

public class Demo02 {
public static void main(String[] args) {

//创建对象
Teacher t1 = new Teacher();
Teacher t2 = new Teacher();
Teacher t3 = new Teacher("柳岩",35);
System.out.println("一共创建过" + Teacher.count +"次");
}
}

 

2 静态代码块(掌握)

  • 静态代码块

    • 格式:

        //静态代码块
      static{

      }
    • 位置:定义在成员位置,类中方法外。

    • 执行:在当前类第一次被使用时,静态代码块会执行。并且静态代码块只会执行一次。

    • 使用场景:比如我们加载驱动(驱动需要在第一次使用前加载,只需要加载一次),这种功能就可以放在静态代码块中。

格式:

public class Person {
private String name;
private int age;
//静态代码块
static{
System.out.println("静态代码块执行了");
}
}

3 局部代码块

  • 格式:

定义在方法中:
//局部代码块
{

}
public class Demo03 {
public static void main(String[] args) {
int a = 10;
//局部代码块
//局部代码块的作用就是限制变量的作用域
{
int b = 20;
}

//int a = 30; //在同一个区域不能定义重名变量
//不报错,因为作用域不同
int b = 40;
}
}

4 执行顺序演示

public class AAA {
//静态代码块
//静态代码块只会执行一次 并在最开始第一个执行
static{
System.out.println(2);
}
//构造代码块
//在每次执行构造方法之前先执行构造代码块
{
System.out.println(3);
}
//构造方法
public AAA() {
System.out.println(1);
}

}

public class Demo04{
public static void main(String[] args) {
//创建对象
AAA a1 = new AAA();
AAA a2 = new AAA();
}
}

执行结果: 2 3 1 3 1

 

 

 

第五章 接口

1 概述

接口,是Java语言中一种引用类型,是方法的集合,如果说类的内部封装了成员变量、构造方法和成员方法,那么接口的内部主要就是封装了方法,包含抽象方法(JDK 7及以前),默认方法和静态方法(JDK 8)。

接口的定义,它与定义类方式相似,但是使用 interface 关键字。它也会被编译成.class文件,但一定要明确它并不是类,而是另外一种引用数据类型。

public class 类名.java-->.class

public interface 接口名.java-->.class

引用数据类型:数组,类,接口。

接口的使用,它不能创建对象,但是可以被实现(implements ,类似于被继承)。一个实现接口的类(可以看做是接口的子类),需要实现接口中所有的抽象方法,创建该类对象,就可以调用方法了,否则它必须是一个抽象类。

 

2 定义格式

public interface 接口名称 {
// 抽象方法
// 默认方法
// 静态方法
}

含有抽象方法

抽象方法:使用abstract 关键字修饰,可以省略,没有方法体。该方法供子类实现使用。

代码如下:

public interface InterFaceName {
public abstract void method();
}

含有默认方法和静态方法

默认方法:使用 default 修饰,不可省略,供子类调用或者子类重写。

静态方法:使用 static 修饰,供接口直接调用。

代码如下:

public interface InterFaceName {
public default void method() {
// 执行语句
}
public static void method2() {
// 执行语句
}
}

小结:定义接口时就是将定义类的class改成了interface,并且接口中的内容也有了一些变化。

 

3 基本的实现

实现的概述

类与接口的关系为实现关系,即类实现接口,该类可以称为接口的实现类,也可以称为接口的子类。实现的动作类似继承,格式相仿,只是关键字不同,实现使用 implements关键字。

非抽象子类实现接口:

  1. 必须重写接口中所有抽象方法。

  2. 继承了接口的默认方法,即可以直接调用,也可以重写。

实现格式:

class 类名 implements 接口名 {
// 重写接口中抽象方法【必须】
// 重写接口中默认方法【可选】
}

抽象方法的使用

必须全部实现,代码如下:

定义接口:

public interface LiveAble {
// 定义抽象方法
public abstract void eat();
public abstract void sleep();
}

定义实现类:

public class Animal implements LiveAble {
@Override
public void eat() {
System.out.println("吃东西");
}

@Override
public void sleep() {
System.out.println("晚上睡");
}
}

定义测试类:

public class InterfaceDemo {
public static void main(String[] args) {
// 创建子类对象
Animal a = new Animal();
// 调用实现后的方法
a.eat();
a.sleep();
}
}
输出结果:
吃东西
晚上睡

默认方法的使用

可以继承,可以重写,二选一,但是只能通过实现类的对象来调用。

  1. 继承默认方法,代码如下:

定义接口:

public interface LiveAble {
public default void fly(){
System.out.println("天上飞");
}
}

定义实现类:

public class Animal implements LiveAble {
// 继承,什么都不用写,直接调用
}

定义测试类:

public class InterfaceDemo {
public static void main(String[] args) {
// 创建子类对象
Animal a = new Animal();
// 调用默认方法
a.fly();
}
}
输出结果:
天上飞
  1. 重写默认方法,代码如下:

定义接口:

public interface LiveAble {
public default void fly(){
System.out.println("天上飞");
}
}

定义实现类:

public class Animal implements LiveAble {
@Override
public void fly() {
System.out.println("自由自在的飞");
}
}

定义测试类:

public class InterfaceDemo {
public static void main(String[] args) {
// 创建子类对象
Animal a = new Animal();
// 调用重写方法
a.fly();
}
}
输出结果:
自由自在的飞

静态方法的使用

静态与.class 文件相关,只能使用接口名调用,不可以通过实现类的类名或者实现类的对象调用,代码如下:

定义接口:

public interface LiveAble {
public static void run(){
System.out.println("跑起来~~~");
}
}

定义实现类:

public class Animal implements LiveAble {
// 无法重写静态方法
}

定义测试类:

public class InterfaceDemo {
public static void main(String[] args) {
// Animal.run(); // 【错误】无法继承方法,也无法调用
LiveAble.run(); //
}
}
输出结果:
跑起来~~~

小结: 类实现接口使用的是implements关键字,并且一个普通类实现接口,必须要重写接口中的所有的抽象方法

 

4 接口的多实现

之前学过,在继承体系中,一个类只能继承一个父类。而对于接口而言,一个类是可以实现多个接口的,这叫做接口的多实现。并且,一个类能继承一个父类,同时实现多个接口。

实现格式:

class 类名 [extends 父类名] implements 接口名1,接口名2,接口名3... {
// 重写接口中抽象方法【必须】
// 重写接口中默认方法【不重名时可选】
}

[ ]: 表示可选操作。

抽象方法

接口中,有多个抽象方法时,实现类必须重写所有抽象方法。如果抽象方法有重名的,只需要重写一次。代码如下:

定义多个接口:

interface A {
public abstract void showA();
public abstract void show();
}

interface B {
public abstract void showB();
public abstract void show();
}

定义实现类:

public class C implements A,B{
@Override
public void showA() {
System.out.println("showA");
}

@Override
public void showB() {
System.out.println("showB");
}

@Override
public void show() {
System.out.println("show");
}
}

默认方法

接口中,有多个默认方法时,实现类都可继承使用。如果默认方法有重名的,必须重写一次。代码如下:

定义多个接口:

interface A {
public default void methodA(){}
public default void method(){}
}

interface B {
public default void methodB(){}
public default void method(){}
}

定义实现类:

public class C implements A,B{
@Override
public void method() {
System.out.println("method");
}
}

静态方法

接口中,存在同名的静态方法并不会冲突,原因是只能通过各自接口名访问静态方法。

优先级的问题

当一个类,既继承一个父类,又实现若干个接口时,父类中的成员方法与接口中的默认方法重名,子类就近选择执行父类的成员方法。代码如下:

定义接口:

interface A {
public default void methodA(){
System.out.println("AAAAAAAAAAAA");
}
}

定义父类:

class D {
public void methodA(){
System.out.println("DDDDDDDDDDDD");
}
}

定义子类:

class C extends D implements A {
// 未重写methodA方法
}

定义测试类:

public class Test {
public static void main(String[] args) {
C c = new C();
c.methodA();
}
}
输出结果:
DDDDDDDDDDDD

小结: 一个类可以实现多个接口,多个接口之间使用逗号隔开即可。

 

5 接口的多继承【理解】

一个接口能继承另一个或者多个接口,这和类之间的继承比较相似。接口的继承使用 extends 关键字,子接口继承父接口的方法。如果父接口中的默认方法有重名的,那么子接口需要重写一次。代码如下:

定义父接口:

interface A {
public default void method(){
System.out.println("AAAAAAAAAAAAAAAAAAA");
}
}

interface B {
public default void method(){
System.out.println("BBBBBBBBBBBBBBBBBBB");
}
}

定义子接口:

interface D extends A,B{
@Override
public default void method() {
System.out.println("DDDDDDDDDDDDDD");
}
}

小贴士:

子接口重写默认方法时,default关键字可以保留。

子类重写默认方法时,default关键字不可以保留。

小结:接口和接口之间是继承的关系,而不是实现。一个接口可以继承多个接口。

 

6 其他成员特点

  • 接口中,无法定义成员变量,但是可以定义常量,其值不可以改变,默认使用public static final修饰。

  • 接口中,没有构造方法,不能创建对象。

  • 接口中,没有静态代码块。

7接口和抽象类

抽象类:
本质是一个类class
抽象类有构造方法,抽象类我们不能创建对象
可以有抽象方法
接口:
本质是一个interface
接口没有构造方法,也不能创建对象
方法不加修饰符,自动就是抽象方法

接口的作用:
弥补了单继承的问题,因为接口可以同时实现多个

共同点:
接口和抽象类都要有子类。

 

8抽象类和接口的综合案例

  • 案例图:

  • 用到的知识点:

    类、抽象类、接口、继承、实现

  • 代码实现:

    //人类
    public abstract class Person {
    //姓名
    String name;
    //年龄
    int age;

    //构造方法
    public Person() {
    }

    public Person(String name, int age) {
    this.name = name;
    this.age = age;
    }
    }
//运动员类
public abstract class Athlete extends Person {

//提供自己的构造方法
public Athlete() {
//super();
}

public Athlete(String name, int age) {
super(name, age);
}

//学习
public abstract void study();
}
//教练类
public abstract class Coach extends Person {
//编号
String id;
//空参构造
public Coach() {
}

//有参构造
public Coach(String name, int age, String id) {
//姓名和年龄赋值给父类
super(name, age);
//编号赋值给自己的成员变量
this.id = id;
}

//教学
//父类中不知道到底教啥 定义成抽象方法 在子类中自己重写
public abstract void teach();
}
//篮球运动员
public class BasketAthlete extends Athlete implements English{

//构造方法

public BasketAthlete() {
}

public BasketAthlete(String name, int age) {
super(name, age);
}

@Override
public void study() {
System.out.println(age +"岁的篮球运动员"+name + "在打篮球");
}

@Override
public void speakEnglish() {
System.out.println("篮球运动员出国说英语");
}
}
//篮球教练
public class BasketCoach extends Coach implements English{
//构造方法
public BasketCoach() {
}

public BasketCoach(String name, int age, String id) {
super(name, age, id);
}

@Override
public void teach() {
System.out.println("编号为" + id + "年龄为" + age + "姓名叫" + name + "的篮球教练教扣篮");
}

@Override
public void speakEnglish() {
System.out.println("篮球教练出国说英语");
}
}
public class FootAthlete extends Athlete {
//构造方法

public FootAthlete() {
}

public FootAthlete(String name, int age) {
super(name, age);
}

@Override
public void study() {
System.out.println(age + "岁的足球运动员" + name + "在踢足球");
}
}
//足球教练
public class FootCoath extends Coach {
//构造方法

public FootCoath() {
}

public FootCoath(String name, int age, String id) {
super(name, age, id);
}

@Override
public void teach() {
System.out.println("编号为" + id + "年龄为" + age + "姓名叫" + name + "的足球教练教射门");
}
}
//接口
public interface English {
//学英语(抽象方法)
void speakEnglish();
}
public class Demo测试类 {
public static void main(String[] args) {
//足球教练
FootCoath fc = new FootCoath("柳岩",20,"001");
//调用方法
fc.teach();


System.out.println("--------------");
//篮球运动员
BasketAthlete ba = new BasketAthlete("蔡徐坤",35);
ba.study();
ba.speakEnglish();
}
}

 

讲完抽象类和接口后,相信有许多同学会存有疑惑,两者的共性那么多,只留其中一种不就行了,这里就得知道抽象类和接口从根本上解决了哪些问题.

一个类只能继承一个直接父类(可能是抽象类),却可以实现多个接口, 接口弥补了Java的单继承

抽象类为继承体系中的共性内容, 接口为继承体系中的扩展功能

接口还是后面一个知识点的基础(lambda)

 

9 接口小结

  • 接口中,无法定义成员变量,但是可以定义常量,其值不可以改变,默认使用public static final修饰。

  • JDK8前接口中的方法全是抽象方法,默认会自动加上public abstract修饰

  • JDK 8开始,接口不再纯洁,支持静态方法,默认方法,私有方法。

  • 接口中,没有构造器,不能创建对象

  • 类与接口是多实现的 implements

  • 接口与接口是多继承的 extends

  • 接口体现的规范。

 

 

第六章 枚举

1 不使用枚举存在的问题

假设我们要定义一个人类,人类中包含姓名和性别。通常会将性别定义成字符串类型,效果如下:

public class Person {
private String name;
private String sex;

public Person() {
}

public Person(String name, String sex) {
this.name = name;
this.sex = sex;
}

// 省略get/set方法
}
public class Demo01 {
public static void main(String[] args) {
Person p1 = new Person("张三", "男");
Person p2 = new Person("张三", "abc"); // 因为性别是字符串,所以我们可以传入任意字符串
}
}

不使用枚举存在的问题:可以给性别传入任意的字符串,导致性别是非法的数据,不安全。

2 枚举的作用与应用场景

枚举的作用:一个方法接收的参数是固定范围之内的时候,那么就可以使用枚举。

  • 交通信号灯案例:

package com.itheima.sh.j_enum_10;
/*
1.定义一个枚举类型
*/
public enum TrafficLight {
//2.在枚举类型的成员位置定义三个属性分别表示红 黄 绿 三种信号灯
RED,YELLOW,GREEN
}

package com.itheima.sh.j_enum_10;

/*
交通信号灯练习:
步骤:
1.定义一个枚举类型
2.在枚举类型的成员位置定义三个属性分别表示红 黄 绿 三种信号灯
3.在测试类中调用自定义方法传递枚举类中的绿灯
4.在自定义方法中使用多分支结构根据接收的信号灯输出结果
*/
public class Test02 {
public static void main(String[] args) {
//3.在测试类中调用自定义方法传递枚举类中的绿灯
change(TrafficLight.GREEN);
}

private static void change(TrafficLight green) {
//4.在自定义方法中使用多分支结构根据接收的信号灯输出结果
switch (green) {
/*
在switch语句中的case后面如果书写的常量是枚举类型,那么不用加枚举类,直接书写
枚举类中的常量属性名
*/
case RED:
System.out.println("红灯停");
break;
case YELLOW:
System.out.println("黄灯等一等");
break;
case GREEN:
System.out.println("绿灯行");
break;
}
}
}

 

说明:在switch语句中的case后面如果书写的常量是枚举类型,那么不用加枚举类,直接书写 枚举类中的常量属性名

 

3 枚举的基本语法

1 枚举的概念

枚举是一种特殊类。枚举是有固定实例个数的类型,我们可以把枚举理解成有固定个数实例的多例模式。

2 定义枚举的格式

enum 枚举名 {
第一行都是罗列枚举实例,这些枚举实例直接写大写名字即可。
}

3 入门案例

  1. 定义枚举:BOY表示男,GIRL表示女

enum Sex {
BOY, GIRL; // 男,女
}
  1. Person中的性别由String类型改为Sex枚举类型

public class Person {
private String name;
private Sex sex;

public Person() {

}

public Person(String name, Sex sex) {
this.name = name;
this.sex = sex;
}
// 省略get/set/toString方法
}
  1. 使用时只能传入枚举中的固定值

public class Demo02 {
public static void main(String[] args) {
Person p1 = new Person("张三", Sex.BOY);
Person p2 = new Person("张三", Sex.GIRL);
Person p3 = new Person("张三", "abc");
}
}

4 枚举的其他内容

我们大概了解了枚举类型的定义与简单使用后,现在有必要来了解一下枚举类型的基本实现原理。 实际上在使用关键字enum创建枚举类型并编译后,编译器会为我们生成一个相关的类,这个类继承了Java API中的java.lang.Enum类,也就是说通过关键字enum创建枚举类型在编译后事实上也是一个类类型而且该类继承了java.lang.Enum类。

我们刚才定义的Sex枚举反编译最终效果如下:

enum Sex {
BOY, GIRL; // 男,女
}
//反编译可以看见
// 枚举的本质是一个类,我们刚才定义的Sex枚举相当于下面的类
final class Sex extends java.lang.Enum<SEX> {
public static final SEX BOY = new SEX();
public static final SEX GIRL = new SEX();
public static SEX[] values();
public static SEX valueOf(java.lang.String);
static {};
}

说明:

1)从反编译的代码可以看出编译器确实帮助我们生成了一个Sex类(注意该类是final类型的,将无法被继承)而且该类继承自java.lang.Enum类,Enum类是一个抽象类 2)除此之外,编译器还帮助我们生成了2个Sex类型的实例对象分别对应枚举中定义的2个性别,这也充分说明了我们前面使用关键字enum定义的Sex类型中的每种性别枚举常量也是实实在在的Sex实例对象。

5 枚举补充

枚举的本质是一个类,所以枚举中还可以有成员变量,成员方法等。

1)枚举的属性上面不能书写任何代码,如果属性下面有代码,需要使用分号结束属性的编写 ​ 2)只要我们使用枚举类型,那么属性属于静态的,并且给属性赋值,会创建对象,执行无参构造方法 ​ 3)如果想执行有参构造,可以给属性后面添加小括号,并赋值实际参数 ​ 4)枚举中的构造函数必须是私有的

public enum Sex {
BOY("柳岩"), GIRL(18);

public int age;
public String name;

private Sex(int age) {
System.out.println("有参构造"+age);
this.age = age;
}
private Sex() {
System.out.println("无参构造");
}
private Sex(String name) {
System.out.println("有参构造"+name);
this.name = name;
}
public void showAge() {
System.out.println("年龄是: " + age);
}
public void showName() {
System.out.println("姓名是: " + name);
}

}
public class Demo03 {
public static void main(String[] args) {
//Person p1 = new Person("张三", Sex.BOY);
// Person p2 = new Person("张三", Sex.GIRL);
//使用枚举类型获取枚举类型中的元素即对象
Sex b = Sex.BOY;
b.showName();
Sex g = Sex.GIRL;
g.showAge();
}
}

执行内容:

有参构造柳岩
有参构造18
姓名是: 柳岩
年龄是: 18

 

4 枚举的应用

枚举的作用:枚举通常可以用于做信息的分类,如性别,方向,季度等。

枚举表示性别:

public enum Sex {
MAIL, FEMAIL;
}

枚举表示方向:

public enum Orientation {
UP, RIGHT, DOWN, LEFT;
}

枚举表示季度

public enum Season {
SPRING, SUMMER, AUTUMN, WINTER;
}

5 小结

  • 枚举类在第一行罗列若干个枚举对象,是常量。(多例)

  • 枚举是不能在外部创建对象的,枚举的构造方法默认是私有的。

  • 枚举通常用于做信息的标志和分类。

  •  
posted @   忘了鱼尾纱的猫  阅读(82)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
  1. 1 刘哈哈与大先生 刘心&大鹏
  2. 2 我们打着光脚在风车下跑,手上的狗尾巴草摇啊摇 等一下就回家 / -艾兜
  3. 3 哎呦 毛不易
  4. 4 夜、萤火虫和你 AniFace
夜、萤火虫和你 - AniFace
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.

轻音乐

点击右上角即可分享
微信分享提示