面向对象
类和接口强转问题
在java中,类和类之间如果有继承关系,那么就可以在编译阶段强转。在运行阶段需要类型必须匹配,否则抛出异常ClassCastException
在类和接口之间,在编译阶段java直接放弃检查,需要程序员自己判断。
public class TestDemo {
public static void main(String[] args) {
// 探讨引用数据类型强制类型转换的问题
// 在Java中,类和类之间支持的是单继承
// 因此可以形成一颗继承结构树
// 所以比较容易确定两个类之间是否有继承关系
A a = new B1();
// 在编译期间会检查对象的声明类型和要强转的类型之间是否有继承关系
// 在编译期间,a对象的声明类型是A类
// a对象要强转的类型是B1,发现B1类和A类之间有继承关系.
// 这里由于a对象的类型A类是B1的父类,所以需要强转才能编译通过
// 在运行阶段,发现a的实际类型是B1,要强转的类型也是B1,类型匹配,可以强转。
// B1 b1 = (B1) a;
// System.out.println(b1);
/*
* a对象的声明类型是A类
* 要强转的类型是B2,发现B2继承了A,所以编译阶段强转可以通过。
* 到了运行阶段,发现a的实际类型是B1,要强转的类型是B2,所以类型不匹配,抛出异常,不可以强转。
* */
// B2 b2 = (B2) a;
// ClassCastException:引用数据类型的类型转换异常
// System.out.println(b2);
// a对象在编译阶段的声明类型是A类,要强转的类型是C类。
// A类和C类之间没有继承关系,所以编译不通过。
// C c = (C)a;
/*
* 在Java中,因为接口之间是多继承以及多实现的。
* 所以形成一个网状结构
* 在编译期间,不容易确定两个节点之间是否有关系
* java为了提高编译效率,在编译期间放弃检查
* 也就意味着,在编译期间,任何一个接口都可以强转
* */
D d = (D)a;
System.out.println(d);
}
}
class A {
int i = 10;
}
class B1 extends A{
}
class B2 extends A{
}
class C{
}
interface D{
}
1 接口的新特性
在jdk1.8之前,接口中全部都是抽象方法。jdk1.8之后可以有静态方法,默认方法,私有方法,私有静态方法。
@FunctionalInterface//函数式接口
public interface MyInterface {
void update();
default boolean defaultMethod1(){
return true;
}
// jdk8中可以使用default修饰实体方法
default void defaultMethod(){
System.out.println("默认方法");
// 调用私有方法
this.privateMethod();
}
// jdk8中可以使用static修饰实体方法
static void method(){
System.out.println("static方法");
MyInterface.privateStaticMethod();
}
// jdk9中可以定义私有的实体方法
private void privateMethod(){
System.out.println("私有的实体方法");
}
private static void privateStaticMethod(){
System.out.println("私有的静态方法");
}
}
2 lambda表达式
lambda表达式是JDK1.8之后的特性,要使用这个表达式,要求接口中只能有一个抽象方法。
可以使用@FunctionalInterface 来标记一个函数式接口。
//表示这个接口是一个函数式接口
// 这个注解表示这个接口中只能有一个抽象方法
// 是一个标记注解(用不用都可以)
// 如果一个接口中只有一个抽象方法,可以使用JDK1.8的特性-->lambda表达式
@FunctionalInterface
public interface MyInterface {
lambda表达式本质上是在替换匿名内部类的语法,让程序员编写代码更加简单。
lambda表示是面向函数式编程,面向接口中的那一个唯一的抽象方法编程
lambda表达式基本语法
(参数) -> {方法体}
lambda表达式可读性比较差
2.1 无参无返回值
@FunctionalInterface
public interface MyInterface {
// 无参无返回值
void hello();
}
public class TestDemo {
public static void main(String[] args) {
// 匿名内部类实际上是接口的实现类或者类的子类
// MyInterface myInterface = new MyInterface() {// 匿名内部类 目前这个没有名字的类是MyInterface接口的实现类
@Override
public void hello() {
System.out.println("hello...");
}
};
myInterface.hello();
// 使用lambda替换匿名内部类 -- 面向函数式编程 -- 面向接口中唯一的抽象方法编程
// MyInterface myInterface = () -> {
// System.out.println("hello...");
// };
// 方法体只有一行,可以省略{}
MyInterface myInterface = () -> System.out.println("hello");
myInterface.hello();
demo(()-> System.out.println("hahaha"));
demo(new MyInterfaceImpl());
}
public static MyInterface demo(MyInterface myInterface){
return () -> System.out.println("111");
}
}
2.2 有参无返回值
- 参数只有一个
@FunctionalInterface
public interface MyInterface {
// 无参无返回值
// void hello();
// 有参无返回值
void compareStr(String str);
}
private static void demo2() {
// 匿名内部类
// MyInterface myInterface = new MyInterface() {
// @Override
// public void compareStr(String str) {
// System.out.println("hello".equals(str));
// }
// };
//
// myInterface.compareStr("hell");
// (参数) -> {方法体}
// MyInterface myInterface = (String str) -> {
// System.out.println("hello".equals(str));
// };
// 方法体只有一条语句,可以省略{}
// MyInterface myInterface = (String str) -> System.out.println("hello".equals(str));
// 如果形参只有一个,那么可以省略数据类型和()
MyInterface myInterface = str -> System.out.println("hello".equals(str));
myInterface.compareStr("hello");
}
- 参数有多个
@FunctionalInterface
public interface MyInterface {
// 无参无返回值
// void hello();
// 有参无返回值
// void compareStr(String str);
void max(int num1,int num2);
}
private static void demo3() {
// 匿名内部类
// MyInterface myInterface = new MyInterface() {
// @Override
// public void max(int num1, int num2) {
// System.out.println("最大值是" + Math.max(num1,num2));
// }
// };
// lambda表达式
// MyInterface myInterface = (int num1,int num2) -> System.out.println("最大值是" + Math.max(num1,num2));
// 如果形参数量大于1个,可以省略数据类型
// 形参名可以随意起名
MyInterface myInterface = (a,b) -> System.out.println("最大值是" + Math.max(a,b));
myInterface.max(15,17);
}
2.3 有参有返回值
@FunctionalInterface
public interface MyInterface {
// 无参无返回值
// void hello();
// 有参无返回值
// void compareStr(String str);
// void max(int num1,int num2);
// 有参有返回值
// int add(int num1,int num2);
// boolean compareStr(String str);
// 定义一个方法,获得Teacher对象中的任意一个属性的数据
Object getPropertyValue(Teacher teacher);
}
private static void demo6() {
// 匿名内部类
// MyInterface myInterface = new MyInterface() {
// @Override // Object o = teacher.getName();
// public Object getPropertyValue(Teacher teacher) {
// return teacher.getName();
// }
// };
// lambda表达式
// MyInterface myInterface = t->t.getName();
MyInterface myInterface = Teacher::getName;
Teacher teacher = new Teacher();
teacher.setId(10);
teacher.setName("冲田老师");
System.out.println(myInterface.getPropertyValue(teacher));
}
private static void demo5() {
// 匿名内部类
// MyInterface myInterface = new MyInterface() {
// @Override
// public boolean compareStr(String str) {
// return "hello".equals(str);
// }
// };
// MyInterface myInterface = str->"hello".equals(str);
// 使用 对象::方法名
// MyInterface myInterface = "hello"::equals;
// System.out.println(myInterface.compareStr(null));
}
private static void demo4() {
// MyInterface myInterface = new MyInterface() {
// @Override
// public int add(int num1, int num2) {
// return Integer.sum(num1,num2);
// }
// };
// lambda表达式
// MyInterface myInterface = (int a,int b) -> {
// return a + b;
// };
// 方法体中有返回值类型,并且语句只有一行,可以省略{return}
// 这种写法使用的比较多
// MyInterface myInterface = (a,b) -> a + b;
// 重写的方法的形参数量和类型以及返回值类型 与 要调用的方法的形参数量和类型和返回值类型一样,就可以使用下面的写法
// lambda究极形态 数据类型::方法名
// MyInterface myInterface = Integer::sum;// return Integer.sum(a,b);
// // Integer是一个包装类 对应的基本数据类型是int
// System.out.println(myInterface.add(1, 2));
}
3 内部类
3.1 概述
将一个类写在其他类的内部,可以写在其他类的成员位置或者局部位置,这时写在其他类内部的类称之为内部类。其他类称之为外部类。
内部类分为:
- 局部内部类/方法内部类
- 成员内部类
- 静态内部类
- 匿名内部类(重要)
3.2 局部内部类
局部内部类也称之为方法内部类,它是定义在方法中的内部类。
public class TestDemo3 {
public static void main(String[] args) {
new Outer1().m();
}
}
class Outer1{//外部类
public void m(){
System.out.println("Outer~~~");
// 局部内部类/方法内部类
class Inner1{
public void m(){
System.out.println("Inner~~~~");
}
}
Inner1 inner1 = new Inner1();
inner1.m();
}
}
- 局部内部类可以使用外部类的所有的属性和方法
class Outer1{//外部类
int i = 5;
static int j = 10;
public static void m1(){
System.out.println("外部类的m1方法");
}
public void m2(){
System.out.println("外部类的m2方法");
}
public void m(){
System.out.println("Outer~~~");
// 局部内部类/方法内部类
class Inner1{
public void m(){
System.out.println("Inner~~~~");
System.out.println(i);
System.out.println(j);
m1();
m2();
}
}
Inner1 inner1 = new Inner1();
inner1.m();
}
}
-
如果局部内部类和外部类出现了相同名称的变量,优先使用内部类的(就近原则)
-
如果局部内部类和外部类出现了相同名称的变量,调用外部类属性的语法:外部类.this.属性名
-
可以继承其他类也可以实现接口
3.3 成员内部类
成员内部类是在类中方法外的内部类。
- 可以使用外部类中的一切属性和方法
- 在成员内部类中不可以定义静态变量和静态方法
- 可以定义非静态变量和非静态方法
- 可以定义静态常量
- 创建格式
创建格式 :外部类.内部类 对象名 = new 外部类().new 内部类();
public class TestDemo3 {
public static void main(String[] args) {
// new Outer1().m(); 局部内部类
// 成员内部类 创建格式 :外部类.内部类 对象名 = new 外部类().new 内部类();
Outer2.Inner2 inner2 = new Outer2().new Inner2();
inner2.m();
}
}
class Outer2{
int i = 8;
static int x = 10;
//成员内部类
// 可以使用外部类中的一切属性和方法
// 不可以定义静态变量和静态方法
class Inner2{
// 采用就近原则
// int i = 10;
// 可以定义静态常量
static final int j = 10;
public void m(){
System.out.println(i);
System.out.println(j);
System.out.println(x);
}
}
}
3.4 静态内部类
被static修饰的类就是静态内部类,外部类不能被static修饰
- 不能使用外部类的非静态属性和非静态方法
- 可以定义一切的属性和方法
- 使用格式
外部类.内部类 对象名 = new 外部类.内部类();
public class TestDemo3 {
public static void main(String[] args) {
// new Outer1().m(); 局部内部类
// 成员内部类 创建格式 :外部类.内部类 对象名 = new 外部类().new 内部类();
// Outer2.Inner2 inner2 = new Outer2().new Inner2();
// inner2.m();
// 静态内部类
// 使用格式: 外部类.内部类 对象名 = new 外部类.内部类();
Outer3.Inner3 inner3 = new Outer3.Inner3();
inner3.m();
// 调用静态内部类的静态方法
// 格式:外部类.内部类.静态方法名();
Outer3.Inner3.m1();
}
}
class Outer3{
static int i = 3;
// 静态内部类
// 不能使用外部类的非静态属性和非静态方法
// 可以定义一切的属性和方法
static class Inner3{
int i1 = 4;
static int j = 8;
public void m(){
System.out.println(i);
}
public static void m1(){
System.out.println("m1是静态方法");
}
}
}
3.5 匿名内部类
匿名内部类就是一个没有名字的内部类,实际上是实现了对象的接口或者是继承了对应的类的内部类。
任何一个接口都可以存在匿名内部类,任何一个可以被继承的类都可以存在匿名内部类。
最终类不能使用匿名内部类。
MyInter myInter = new MyInter() { //匿名内部类 这里表示的是MyInter接口的实现类
@Override
public void m() {
System.out.println("m方法");
}
};
myInter.m();
B b = new B(){ //匿名内部类 表示是抽象类B的子类
@Override
public void m1() {
System.out.println("m1");
}
};
b.m1();
A a = new A(){
// 匿名内部类 这个类是A的子类
@Override
public void m2() {
System.out.println("重写m2方法");
}
};
}
}
class A {
public void m2(){
}
}
// 抽象类
abstract class B{
public abstract void m1();
}
interface MyInter{
void m();
}
4 JDK5部分特性
JDK5是java中的一个重要版本,出现了很多好用的特性。
例如;增强for循环、自动封箱拆箱、泛型、静态导入、可变参数、枚举、动态代理、注解
4.1 静态导入(了解)
导入已经有的静态方法。
// 静态导包 表示导入Arrays类中的sort方法 比如导入的是静态方法
import static java.util.Arrays.sort;
// *代表通配符 表示导入java.util包下的所有的类,但是不包括子包下的类
import java.util.*;
4.2 可变参数
可变参数就是用 数据类型… 声明的参数,特点是参数的个数不限。
可变参数只能定义一个
如果有多个参数,可变参数必须放在参数列表的末尾。
可变参数本质上就是一个数组
可变参数是一个语法糖。
public static void main(String[] args) {
// int[] arr = {1,2,3,4,5};
// System.out.println(add(arr));
System.out.println(add(1, 2, 3, 4, 5, 6));
}
// 用 数据类型... 声明一个可变参数
// 定义一个求和方法,求不同数字的和
public static int add(int a,int b,int... array){
int sum = 0;
for (int i = 0; i < array.length; i++) {
sum += array[i];
}
return sum;
}
4.3 枚举
枚举中的值都是固定的内容,不会多也不会少,不允许类外随便修改。
4.3.1 上古时代枚举类(了解)
// 定义一个季节的枚举类
class Season{
// Season对象只能够创建4个,只能是春夏秋冬,那么构造方法就不能暴露在外面。否则外面可以随意创建
// 所以使用private修饰
private Season(){}
// 这时可以在本类中创建
// static保证外面通过类名可以使用
// final保证外面类中无法修改值
// public保证可以在任意地方访问
public static final Season spring = new Season();
public static final Season summer = new Season();
public static final Season autumn = new Season();
public static final Season winter = new Season();
}
4.3.2 枚举类定义
枚举类声明格式:
enum 枚举类名{
枚举的常量,枚举的常量
}
package cn.javasm.demo6;
/**
* @version 0.1
* @className: TestDemo
* @author: gfs
* @description
* @date: 2023/8/22 17:39
* @since jdk11
*/
public class TestDemo {
public static void main(String[] args) {
// 从jdk5开始,允许switch-case语句中使用枚举常量
Season season = Season.SUMMER;
switch (season) {
case SPRING:
Season.SPRING.play();
break;
case SUMMER:
System.out.println("夏天去水上乐园");
break;
case AUTUMN:
Season.AUTUMN.play();
break;
case WINTER:
System.out.println("冬天打雪仗");
break;
}
}
}
// 定义一个枚举类
enum Season {
// 枚举常量要求必须在枚举类有效代码的首行
// 这里的SPRING = public static final Season SPRING = new Season(7){};
SPRING(3) {//匿名内部类
@Override
public void play() {
System.out.println("春天放风筝");
}
},
SUMMER(4) {
@Override
public void play() {
System.out.println("夏天玩水");
}
},
AUTUMN(5) {
@Override
public void play() {
System.out.println("秋天摘果子");
}
},
WINTER(6) {
@Override
public void play() {
System.out.println("冬天滑雪");
}
};
// 枚举类也是一个类,可以定义属性和方法
private int month;
public int getMonth() {
return month;
}
public void setMonth(int month) {
this.month = month;
}
// 枚举中的构造方法必须使用private修饰
private Season(int month) {
this.month = month;
}
// 枚举中可以提供抽象方法
public abstract void play();
}