7、面向对象编程(下)

interface接口

接口,实际上可以看做是一种规范

说明

public interface MyInterface {

    // 全局常量
    String MSG = "hello interface";

    // 抽象方法
    void method1();

    // 静态方法
    static void method2(){
        System.out.println(MSG);
    }

    // 默认方法
    default void method3(){
        System.out.println(MSG);
    }
}

Java8中关于接口的新规范

接口和抽象类的区别

  1. 接口中不能写实例属性,但是抽象类中可以写实例属性,如果说在父类中想定义一些实例属性体现所有子类通用的属性,那么只能选择使用抽象类,如果没有上述需求,接口和抽象类都可以,那么优先使用接口,因为接口会更灵活一些。
  2. 抽象类中可以写构造方法,接口没有构造器
  3. 接口和类之间的关系是实现关系,不一定满足is a的原则,但是抽象类是属于继承体系,需要满足is a的原则
  4. 接口和接口之间可以有继承关系,并且是多继承,类和类之间的继承关系是单继承

类的结构五:内部类

定义:Java中允许将一个类A声明在另一个类B中,则类A就是内部类,类B称为外部类

内部类的分类

成员内部类(静态、非静态)

一方面,作为外部类的成员:

另一方面,作为一个类:

非静态

可以访问外部类所有非静态成员和静态成员,不受权限修饰符影响

public class OuterClass {
    // 可以使用权限修饰符
    private class InerClass{

    }
}

// 实例化,由于属于外部类的非静态类成员,所以需要外部类的实例
OuterClass outerClass = new OuterClass();
InerClass inerClass = outerClass.new InerClass();
静态

只能访问外部类的静态成员,不受权限修饰符影响

public class OuterClass {

    private static class InerClass{

    }
}

// 实例化,属于外部类的静态成员,所以需要类名.来访问
OuterClass.InerClass inerClass = new OuterClass.InerClass();

局部内部类(方法内、代码块内、构造器内)

可以直接访问其所在的外部类的所有非静态的成员和静态的成员,不受权限修饰符影响

public class OuterClass {

    public void test(){
        // 不可以使用权限修饰符
        class InnerClass{

        }
        // 实例化
        InnerClass innerClass = new InnerClass();
    }
}
匿名内部类

匿名内部类是成员内部类的一种,只是一种对接口(或抽象类)实现的形式

public interface MyInterface {
    
    void func1();
    
    void func2();
}
public class OuterClass {

    public static void test(MyInterface i){

    }

    public static void main(String[] args) {
        // 匿名内部类实现接口
        test(new MyInterface(){

            @Override
            public void func1() {

            }

            @Override
            public void func2() {

            }
        });
    }
}

字节码文件的区别

成员内部类和局部内部类,在编译以后,都会生成字节码文件。格式:

作用

  1. 可以无条件地访问外部类的所有成员
  2. 隐藏程序实现细节
  3. 可以实现多重继承(并不是一个内部类可以继承多个),可以声明多个内部类分别继承不同的父类
  4. 对于简单的接口实现进行优化

关键字:native

使用native关键字说明这个方法是原生函数,也就是这个方法是用 C/C++等非 Java 语言实现的,并且被编译成了动态链接库DLL,由java去调用

为什么要用native方法

简单使用

1、编写java程序

public class JniDemo {

    // native方法和abstract修饰的方法一样,只有签名
    public static native void test();

    static {
        // 加载JniDemo.dll,不写文件的后缀,程序会自动加上.dll
        System.loadLibrary("JniDemo");
    }

    public static void main(String[] args) {
        // 调用方法
        test();
    }
}

2、编译并生成头文件

# 编译,产生JniDemo.class
javac -encoding utf-8 JniDemo.java

#生成.h头文件
javah -jni JniDemo

打开生成的头文件JniDemo.h

JniDemo.java文件中的test()方法已经变成了JNIEXPORT void JNICALL Java_JniDemo_test(JNIEnv *, jclass);,方法名是原来的包名_类名_方法名

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class JniDemo */

#ifndef _Included_JniDemo
#define _Included_JniDemo
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     JniDemo
 * Method:    test
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_JniDemo_test
  (JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif

3、编写cpp文件

#include <stdio.h>
#include "JniDemo.h"

JNIEXPORT void JNICALL Java_JniDemo_test (JNIEnv *, jclass){
    printf("Hello Jni From Cpp!");
}

4、编译生成动态链接库

g++ -m64 -I"E:/Java/jdk1.8.0_301/include" -I"E:/Java/jdk1.8.0_301/include/win32" -shared -o JniDemo.dll JniDemo.cpp

5、运行Java程序

java JniDemo
Hello Jni From Cpp!