接口和包--Java学习笔记

时间:2022-04-07 10:48:09

接口

定义及基础用法

interface定义:没有字段的抽象类

interface person{
void hello();
String getName();
}
/*接口本质上就是抽象类
abstract class person{
public abstract void fun();
public abstract String getName();
}
*/

如上代码,方法没有具体化,在需要调用的类里面通过覆写来实现具体功能,多个类之间重新覆写的功能相互不影响。继承接口时,一定要覆写接口里的所有方法

class student implements person{
private String name;
public student(String name){
this.name = name;
}
@override
public void hello(){
System.out.print("hello, " + this.name);
}
@override
public String getName(){
return this.name;
}
}
class teacher implements person{
private String name;
public student(String name){
this.name = name;
}
@override
public void hello(){
System.out.print("hello, Mr/Mrs." + this.name);
}
@override
public String getName(){
return this.name;
}
}

多继承

普通类继承中,只能继承一个类,而接口可实现多继承

class teacher implements person, parents{/**/}

一个interface可以继承自另一个interface

interface hello{
void hello();
}
interface person extends hello{
void fun();
String getName();
//此时,person接口实际上有三个抽象方法前面,其中一个继承自hello
}

abstract和interface对比

abstract

  • 定义实例字段
  • 定义抽象方法
  • 定义非抽象方法
  • but,只能extends一个class

interface

  • 可以implements多个interface
  • 定义抽象方法
  • 定义default方法
  • but,不能定义实例字段

default方法

public class aa {
public static void main(String[] args) {
person p = new student("aaaaa");
p.fun();
}
}
interface person {
String getName();
default void fun() {
System.out.println(getName() + " fun()");
}//default修饰具体方法
}
class student implements person {
private String name;
public student(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
//在这个例子中,student类就没有去重新覆写fun()函数
}

当接口新增一个方法时,会涉及到修改全部子类,如果新增的是default方法,子类就不必全部修改,只需要在需要覆写的地方去覆写新增方法

default和抽象类的普通方法是不太一样的,interface没有字段,default无法访问字段,而抽象类的普通方法可以访问实例字段。不过在interface中,default可以修饰具体方法

静态字段和静态方法

public class aa {
public static void main(String[] args) {
person.setNumber(99);
System.out.println(person.number);
}
}
class person {
public static int number;
public static void setNumber(int value) {
number = value;
}
}

静态方法属于class不属于实例,可以直接通过类名来调用

接口的静态字段

尽管interface是纯抽象类,但它可以有静态字段,静态字段必须是final类型

//person.java
public interface person{
public static final int male = 1;
public static final int female = 2;
}

实际上,因为interface的字段只能是public static final类型,所以我们可以把int前面的修饰符去掉

//person.java
public interface person{
int male = 1;
int female = 2;
//编译器会自动把该字段变为public static final类型
}

例如,在同一文件夹下,a写了一个person类,b也写了一个person类,c想用a和b的person类,这时候引入包(package)的概念

aa.java

package hello;
class person{}
public class aa{}

bb.java

package hello;
class person{}
public class bb{}

包可以是多层结构,用.隔开,例如java.util

:::tip

包没有父子关系,java.utiljava.util.zip是不同的包,两者没有继承关系

:::

包作用域

例如,如果想要定义包hello,则需要新建一个文件夹并命名为hello,文件夹下存放有hello包的Java文件

hello\person.java

package hello;
public class person{
void hello(){
System.out.println("hello");
}
}

hello\main.java

package hello;
public class main{
public static void main(String[] args){
person p = new person();
p.hello();//result: hello
}
}

import

在一个class中,我们会引用其他的class,例如,Jay的person.jay类,如果要引用jjlin的hello.jjlin类,需要引入这个包

hello\jjlin.java

//jjlin.java
package hello;
public class jjlin{
public void fun(){
System.out.println("hello");
}
}

person\jay.java

//jay.java
package person;
import hello.jjlin;//表示引入hello文件夹下的jjlin文件
public class jay{
public void run(){
jjlin temp = new jjlin();
}
}

除了引入包下的具体类,也可以使用*,把包下的所有class都导入进来(不辨认子包的class)

package person;
import jay.*;//引入jay文件夹下所有class
public class jay{
public void run(){
jjlin temp = new jjlin();
}
}

import static

此方法可以导入一个类的静态字段和静态方法,此语法比较少用

//package main;
import static java.lang.System.*;
//导入System类的所有静态字段和静态方法
public class main{
public static void main(String[] args){
out.println("hello");
//如果不引用System包,则要写成下面形式
//System.out.println("hello");
}
}

Java编译器最终编译出的.class文件只是用完整类名,因此,在代码中,当编译器遇到一个class名称时:

  • 如果是完整类名,就直接根据完整类名来查找这个class
  • 如果是简单类名,依次按照下面的顺序依次查找
    • 查找当前package是否存在这个class
    • 查找import包是否含有这个class
    • 查找java.lang包是否含有这个class

如果按照上面规则还无法确定类名,则编译报错

下面是一个例子

//main.java
package test;
import java.text.Format;
public class main{
public static void main(String[] args){
java.util.list list;//ok,使用完整类名
Format format = null;//ok,使用import的类
String s = "hi";//ok,使用java.lang的包的String
System.out.println(s);//ok,使用java.lang的包的System
MessageFormat mf = null;//错误,无法找到MessageFormat
}
}

因此在编写class时,编译器会自动帮我们做两个import动作

  • 默认自动import当前package的其他class
  • 默认import java.lang.*

自动导入的是java.lang的包,但类似java.lang.reflect这些包还需要手动导入

最佳实践

为避免名字冲突,我们需要确定唯一的包名,推荐使用倒置的域名来确保唯一性

  • org.apache,本质上是/org/apache/路径下的class,下同
  • org.apache.commons.log
  • com.jayden.sample

子包就可以根据功能自行命名

注意不要和java.lang的包的类重名

  • String
  • System
  • Runtime
  • ...

也不要和jdk常用的类重名

  • java.util.List
  • java.text.Format
  • java.math.BigInteger
  • ...

参考链接-廖雪峰-Java-面向对象编程-接口

参考链接-廖雪峰-Java-面向对象编程-包