Java 接口
接口(英文:Interface),在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。
接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。
除非实现接口的类是抽象类,否则该类要定义接口中的所有方法。
接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。另外,在 Java 中,接口类型可用来声明一个变量,他们可以成为一个空指针,或是被绑定在一个以此接口实现的对象。
接口与类相似点:
- 一个接口可以有多个方法。
- 接口文件保存在 .java 结尾的文件中,文件名使用接口名。
- 接口的字节码文件保存在 .class 结尾的文件中。
- 接口相应的字节码文件必须在与包名称相匹配的目录结构中。
接口与类的区别:
- 接口不能用于实例化对象。
- 接口没有构造方法。
- 接口中所有的方法必须是抽象方法。
- 接口不能包含成员变量,除了 static 和 final 变量。
- 接口不是被类继承了,而是要被类实现。
- 接口支持多继承。
接口特性
- 接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错)。
- 接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量(并且只能是 public,用 private 修饰会报编译错误)。
- 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法。
抽象类和接口的区别
-
抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。
-
抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。
-
接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。
-
一个类只能继承一个抽象类,而一个类却可以实现多个接口。
package com.xxxx.interface02;
/*
接口:
使用interface关键字定义接口
定义标准,定义规范
接口需要通过implements实现
优点:
是一个特殊的抽象类
是一个引用数据类型(类,接口,数组)
实现解耦 耦合度
类只能单继承 ,接口多实现
更便于后期维护
定义开发规范
功能(抽象方法)的集合
注意:
父类需要被子类继承 ,接口需要被类实现 implements
继承: 子类一旦继承父类,就可以直接使用父类的成员
实现: 接口中一般都是大量的抽象方法,必须要被实现,重写方法才可以使用,实现一个接口,需要重写抽象方法,侧重点在于实现
实现与继承非常像,实现也好,继承也好,都能够有权使用接口|父类中的成员|功能,但是侧重点不一样
接口中不能定义普通的方法,大部分为抽象方法
接口不能实例化
使用:
实现类实现接口,重写所有的抽象方法,通过实现类对象使用接口中的内容
抽象类或者接口的使用,最终都需要具体的子类|实现类,根据子类或者实现类的对象调用
一个类可以同时继承父类,并且实现多个接口,先继承后实现
类继承一个父类,类需要只能实现接口
接口不能实现接口,接口只能继承接口,可以多继承
定义:
jdk1.7版本及之前:
公共的静态的常量:
public static final 数据类型 常量名 = 赋值;
修饰符可以任意省略 public static final
公共的抽象的方法
public abstract 返回值类型 方法名(参数列表);
修饰符可以任意省略 public abstract
jdk1.8版本及之后:
*/
public interface InterfaceDemo01 {
//公共的静态的常量:
public static final double PI = 3.14;
public abstract void haha();
public abstract void hehe();
}
interface A{
void a();
}
interface B{
void b();
}
interface C{}
//接口可以多继承
interface D extends A,B,C{}
package com.xxxx.interface02;
//接口的实现类
public class InterfaceImpl02 implements InterfaceDemo01{
@Override
public void haha() {
System.out.println("哈哈哈笑");
}
@Override
public void hehe() {
System.out.println("呵呵呵笑");
}
//新增内容
public void heihei() {
System.out.println("嘿嘿嘿笑");
}
}
//抽象实现类
abstract class InterfaceImpl03 implements InterfaceDemo01{
@Override
public void haha() {
System.out.println("哈哈哈笑");
}
//public abstract void hehe();
}
//实现多个接口的案例
class Demo extends Object implements A,B,C{
@Override
public void a() {
}
@Override
public void b() {
}
}
class Demo2 implements D{
@Override
public void a() {
}
@Override
public void b() {
}
}
package com.xxxx.interface02;
/*
jdk1.8版本及之后:(了解)
1.静态方法
但是只能通过接口名使用,实现类对象不能使用
2.默认方法
通过实现类对象使用默认方法
*/
public interface InterfaceDemo03 {
//静态方法
public static void testStatic(){
System.out.println("接口中的静态方法");
}
//默认方法
default void testDefault(){
System.out.println("默认方法");
}
}
//实现类
class Impl implements InterfaceDemo03{
}
一.内部类基础
在 Java 中,可以将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类。广泛意义上的内部类一般来说包括这四种:成员内部类、局部内部类、匿名内部类和静态内部类。下面就先来了解一下这四种内部类的用法。
1.成员内部类
成员内部类是最普通的内部类,它的定义为位于另一个类的内部,形如下面的形式:
class Circle {
double radius = 0;
public Circle(double radius) {
this.radius = radius;
}
class Draw { //内部类
public void drawSahpe() {
System.out.println("drawshape");
}
}
}
这样看起来,类Draw像是类Circle的一个成员,Circle称为外部类。成员内部类可以无条件访问外部类的所有成员属性和成员方法(包括private成员和静态成员)。
class Circle {
private double radius = 0;
public static int count =1;
public Circle(double radius) {
this.radius = radius;
}
class Draw { //内部类
public void drawSahpe() {
System.out.println(radius); //外部类的private成员
System.out.println(count); //外部类的静态成员
}
}
}
不过要注意的是,当成员内部类拥有和外部类同名的成员变量或者方法时,会发生隐藏现象,即默认情况下访问的是成员内部类的成员。如果要访问外部类的同名成员,需要以下面的形式进行访问:
外部类.this.成员变量
外部类.this.成员方法
虽然成员内部类可以无条件地访问外部类的成员,而外部类想访问成员内部类的成员却不是这么随心所欲了。在外部类中如果要访问成员内部类的成员,必须先创建一个成员内部类的对象,再通过指向这个对象的引用来访问:
class Circle {
private double radius = 0;
public Circle(double radius) {
this.radius = radius;
getDrawInstance().drawSahpe(); //必须先创建成员内部类的对象,再进行访问
}
private Draw getDrawInstance() {
return new Draw();
}
class Draw { //内部类
public void drawSahpe() {
System.out.println(radius); //外部类的private成员
}
}
}
成员内部类是依附外部类而存在的,也就是说,如果要创建成员内部类的对象,前提是必须存在一个外部类的对象。创建成员内部类对象的一般方式如下:
public class Test {
public static void main(String[] args) {
//第一种方式:
Outter outter = new Outter();
Outter.Inner inner = outter.new Inner(); //必须通过Outter对象来创建
//第二种方式:
Outter.Inner inner1 = outter.getInnerInstance();
}
}
class Outter {
private Inner inner = null;
public Outter() {
}
public Inner getInnerInstance() {
if(inner == null)
inner = new Inner();
return inner;
}
class Inner {
public Inner() {
}
}
}
内部类可以拥有 private 访问权限、protected 访问权限、public 访问权限及包访问权限。比如上面的例子,如果成员内部类 Inner 用 private 修饰,则只能在外部类的内部访问,如果用 public 修饰,则任何地方都能访问;如果用 protected 修饰,则只能在同一个包下或者继承外部类的情况下访问;如果是默认访问权限,则只能在同一个包下访问。这一点和外部类有一点不一样,外部类只能被 public 和包访问两种权限修饰。我个人是这么理解的,由于成员内部类看起来像是外部类的一个成员,所以可以像类的成员一样拥有多种权限修饰。
2.局部内部类
局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内。
class People{
public People() {
}
}
class Man{
public Man(){
}
public People getWoman(){
class Woman extends People{ //局部内部类
int age =0;
}
return new Woman();
}
}
注意: 局部内部类就像是方法里面的一个局部变量一样,是不能有 public、protected、private 以及 static 修饰符的。
3.匿名内部类
匿名内部类应该是平时我们编写代码时用得最多的,在编写事件监听的代码时使用匿名内部类不但方便,而且使代码更加容易维护。下面这段代码是一段 Android 事件监听代码:
scan_bt.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
}
});
history_bt.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
}
});
这段代码为两个按钮设置监听器,这里面就使用了匿名内部类。这段代码中的:
new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
}
}
就是匿名内部类的使用。代码中需要给按钮设置监听器对象,使用匿名内部类能够在实现父类或者接口中的方法情况下同时产生一个相应的对象,但是前提是这个父类或者接口必须先存在才能这样使用。当然像下面这种写法也是可以的,跟上面使用匿名内部类达到效果相同。
private void setListener()
{
scan_bt.setOnClickListener(new Listener1());
history_bt.setOnClickListener(new Listener2());
}
class Listener1 implements View.OnClickListener{
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
}
}
class Listener2 implements View.OnClickListener{
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
}
}
这种写法虽然能达到一样的效果,但是既冗长又难以维护,所以一般使用匿名内部类的方法来编写事件监听代码。同样的,匿名内部类也是不能有访问修饰符和 static 修饰符的。
匿名内部类是唯一一种没有构造器的类。正因为其没有构造器,所以匿名内部类的使用范围非常有限,大部分匿名内部类用于接口回调。匿名内部类在编译的时候由系统自动起名为 Outter$1.class。一般来说,匿名内部类用于继承其他类或是实现接口,并不需要增加额外的方法,只是对继承方法的实现或是重写。
4.静态内部类
静态内部类也是定义在另一个类里面的类,只不过在类的前面多了一个关键字static。静态内部类是不需要依赖于外部类的,这点和类的静态成员属性有点类似,并且它不能使用外部类的非static成员变量或者方法,这点很好理解,因为在没有外部类的对象的情况下,可以创建静态内部类的对象,如果允许访问外部类的非static成员就会产生矛盾,因为外部类的非static成员必须依附于具体的对象。
public class Test {
public static void main(String[] args) {
Outter.Inner inner = new Outter.Inner();
}
}
class Outter {
public Outter() {
}
static class Inner {
public Inner() {
}
}
}
二.深入理解内部类
1.为什么成员内部类可以无条件访问外部类的成员?
在此之前,我们已经讨论过了成员内部类可以无条件访问外部类的成员,那具体究竟是如何实现的呢?下面通过反编译字节码文件看看究竟。事实上,编译器在进行编译的时候,会将成员内部类单独编译成一个字节码文件,下面是 Outter.java 的代码:
public class Outter {
private Inner inner = null;
public Outter() {
}
public Inner getInnerInstance() {
if(inner == null)
inner = new Inner();
return inner;
}
protected class Inner {
public Inner() {
}
}
}
Java Lambda 表达式
java8的新特性 lambda表达式
Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。
Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。
使用 Lambda 表达式可以使代码变的更加简洁紧凑。
语法
lambda 表达式的语法格式如下:
(parameters) -> expression 或 (parameters) ->{ statements; }
以下是lambda表达式的重要特征:
- **可选类型声明:**不需要声明参数类型,编译器可以统一识别参数值。
- **可选的参数圆括号:**一个参数无需定义圆括号,但多个参数需要定义圆括号。
- **可选的大括号:**如果主体包含了一个语句,就不需要使用大括号。
- **可选的返回关键字:**如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。
Lambda 表达式实例
Lambda 表达式的简单例子:
// 1. 不需要参数,返回值为 5
() -> 5
// 2. 接收一个参数(数字类型),返回其2倍的值
x -> 2 * x
// 3. 接受2个参数(数字),并返回他们的差值
(x, y) -> x – y
// 4. 接收2个int型整数,返回他们的和
(int x, int y) -> x + y
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)
(String s) -> System.out.print(s)
package com.xxxx.lambda05;
/*
java8的新特性 lambda表达式 *****
目的: 为了简化匿名内部类对象
匿名内部类: 简化实现类|子类
lambda的前提:
函数式接口
函数式接口: 只有一个必须被重写的抽象方法的接口
检查函数式接口的方式: @FunctionalInterface
lambda表达式结构:
()->{}
(): 重写的抽象方法的参数列表
->: 箭头函数 lambda符号 具有上下文推到的作用
{}: 重写的抽象方法的方法体
*/
public class LambdaDemo01 {
public static void main(String[] args) {
//匿名内部类的对象
/* Demo demo = new Demo(){
@Override
public void test() {
System.out.println("hahahaha");
}
};*/
//lambda的接口简化匿名内部类对象的写法
//写法1.lambda写法1
/*Demo demo =()->{
System.out.println("哈哈哈笑");
};*/
//写法2.当抽象抽象方法的方法体语句只有一句的时候,前后的{}可以省略
//Demo demo =()->System.out.println("哈哈哈笑");
//写法3.抽象方法有参数,参数的数据类型可以省略
//Demo demo =(a,b)->System.out.println("哈哈哈笑");
//写法4.抽象方法的参数只有一个,前后的()可以省略
//Demo demo =a->System.out.println("哈哈哈笑");
//写法5.抽象方法有返回值类型,方法的语句体只有一个,并且是return语句的时候,前后的{}与return关键字可以一起省略
/*Demo demo = a ->{
return "fanhuizhi";
};*/
Demo demo = a ->"fanhuizhi";
System.out.println(demo.test(1));;
}
}
//接口
@FunctionalInterface
interface Demo{
String test(int a);
}
使用 Lambda 表达式需要注意以下两点:
- Lambda 表达式主要用来定义行内执行的方法类型接口,例如,一个简单方法接口。在上面例子中,我们使用各种类型的Lambda表达式来定义MathOperation接口的方法。然后我们定义了sayMessage的执行。
- Lambda 表达式免去了使用匿名方法的麻烦,并且给予Java简单但是强大的函数化的编程能力。
变量作用域
lambda 表达式只能引用标记了 final 的外层局部变量,这就是说不能在 lambda 内部修改定义在域外的局部变量,否则会编译错误。
在 Java8Tester.java 文件输入以下代码:
Java8Tester.java 文件
public class Java8Tester { final static String salutation = "Hello! "; public static void main(String args[]){ GreetingService greetService1 = message -> System.out.println(salutation + message); greetService1.sayMessage(“Runoob”); } interface GreetingService { void sayMessage(String message); } }
执行以上脚本,输出结果为:
$ javac Java8Tester.java
$ java Java8Tester
Hello! Runoob
我们也可以直接在 lambda 表达式中访问外层的局部变量:
Java8Tester.java 文件
public class Java8Tester { public static void main(String args[]) { final int num = 1; Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num)); s.convert(2); // 输出结果为 3 } public interface Converter<T1, T2> { void convert(int i); } }
lambda 表达式的局部变量可以不用声明为 final,但是必须不可被后面的代码修改(即隐性的具有 final 的语义)
int num = 1;
Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num));
s.convert(2);
num = 5;
//报错信息:Local variable num defined in an enclosing scope must be final or effectively
final
在 Lambda 表达式当中不允许声明一个与局部变量同名的参数或者局部变量。
String first = "";
Comparator<String> comparator = (first, second) -> Integer.compare(first.length(), second.length()); //编译会出错