文章链接http://wegocoding.top/article/java-07/
多态
Java中,多态是指不同类的对象在调用同一个方法时所呈现出的多种不同的行为。
Java多态性是由类的继承、方法重写以及父类引用变量指向子类对象体现的。
多态的体现
abstract class Animal {
abstract void shout();
}
class Cat extends Animal {
public void shout() {
System.out.println("喵喵...");
}
}
class Dog extends Animal {
public void shout() {
System.out.println("旺旺...");
}
}
public class Example15 {
public static void main(String[] args) {
Animal a1 = new Cat();
Animal a2 = new Dog();
a1.shout();
a2.shout();
}
}
创建的Cat和Dog两个对象同时指向一个父类对象,并调用shout方法,程序在编译时自动识别具体的子类对象,从而选择性的调用对应的方法。
对象类型转换
“向上转型”:Java中将子类对象当做父类类型使用的情况。
Animal a1 = new Cat(); // 将Cat类对象当做Animal类型使用
Animal a2 = new Dog(); // 同理
...
...
interface Animal {
void shout();
}
class Cat implements Animal {
@Override
public void shout() {
System.out.println("喵喵。。。");
}
public void catchMouse() {
System.out.println("猫抓老鼠。");
}
}
public class Example {
public static void main(String[] args) {
Animal a = new Cat();
a.shout();
a.catchMouse(); // 报错,没有定义该方法
}
}
Tiips
- 不需要任何显式声明
- 不能通过父类变量调用子类特有方法(因为向上转型)
要想调用子类特有方法,可以将父类类型的对象a强转为Cat类型。即“向下转型”。
Cat cat = (Cat) a;
cat.catchMouse();
注意
向下转型必须转换为本质类,否则出现错误。
判断本质类格式:对象或对象引用 instanceof 类或接口
if (a instanceof Cat) {
Cat cat = (Cat) a;
cat.shout();
}
else {
System.out.println("该类型的对象不是Cat类型!");
}
内部类
成员内部类
一个类中除了可以定义成员变量、方法,还可以定义类。
- 成员内部类中,可以访问外部类的所有成员(成员变量和方法)
- 外部类中,可以访问成员内部类的变量和方法
class Outer { // 定义外部类
int m = 0;
void test1() {
System.out.println("外部类成员方法!");
}
class Inner { // 定义内部类
int n = 0;
void show1() {
System.out.println("调用外部类成员变量m=" + m);
}
void show2() {
System.out.println("内部类成员方法!");
}
}
void test2() {
Inner inner = new Inner();
System.out.println("外部类中调用内部类成员变量n=" + inner.n);
inner.show2();
}
}
public class Example18 {
public static void main(String[] args) {
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner(); // 创建内部类对象
inner.show1();
outer.test2();
}
}
创建内部类对象语法
Outer.Inner inner = outer.new Inner();
静态内部类
- 使用
static
关键字修饰的成员内部类 - 静态内部类中,只能访问
外部类的静态成员
- 可直接通过外部类创建
Outer.Inner inner = new Outer.Inner();
局部内部类
在方法中定义的类,有效范围只限于方法内部。
- 在局部内部类中可访问外部类的所有成员变量和方法
- 局部类中的变量和方法只能在创建该局部内部类的方法中进行访问
class Outer {
...
void test() {
// 1.创建局部内部类
class Inner {
int n = 1;
viod show() {
// 局部内部类的方法
...
}
}
// 2.在创建局部内部类的test方法中,调用局部内部类的成员
Inner inner = new Inner();
print(inner.n)
inner.show()
}
}
匿名内部类
匿名内部类就是没有名称的内部类,在调用包含有接口类型参数的方法时,通常为简化代码,不会创建一个接口的实现类作为方法参数传入,而是直接通过匿名内部类的形式传入一个接口类型参数,在匿名内部类中直接完成方法的实现。
// 定义动物类接口
interface Animal {
void shout();
}
public class Example21 {
// 定义静态方法,接收接口类型对象
public static void animalShout(Animal animal) {
animal.shout();
}
public static void main(String[] args) {
String name = "小花";
// 定义匿名内部类作为参数传递给animalShout方法
animalShout(new Animal() {
public void shout() {
System.out.println(name + "喵喵...");
}
});
}
}
Lambda表达式
Lambda表达式只针对有一个抽象方法的接口实现,以简洁的表达形式实现接口功能来作为方法参数。
语法格式:
([dataType var1, dataType var2,...]) -> {expression body}
([dataType var1, dataType var2,...])
:用来向表达式主体传递接口方法需要的参数,参数类型可省略。->
:用来指定参数数据指向{expression body}
:表达式主体,接口中抽象方法的实现;允许有返回值
上例代码用Lambda表达式改写:
...
// 1. 使用匿名内部类作为参数传递
animalShout(new Animal() {
public void shout() {
System.out.println(name + "喵喵...");
}
});
// 2.使用Lambda表达式作为参数
animalShout(()-> System.out.println("Lambda表达式:" + name + "喵喵..."));
...
函数式接口
- Lambda的局限:接口中有且仅有一个抽象方法时才能使用Lambda表达式代替匿名内部类。
- 原因是Lambda是基于函数式接口实现的,即指有仅有一个抽象方法的接口,Lambda是java中函数式编程的体现。
@FunctionalInterface
:注解,显式地标注了接口是一个函数式接口,强制编辑器进行更严格检查,确保是函数式接口。
函数式接口的定义与使用
// 定义无参、无返回值的函数式接口
@FunctionalInterface
interface Animal { void shout();}
// 定义有参、有返回值的函数式接口
interface Calculate { int sum(int a, int b);}
public class Example22 {
public static void main(String[] args) {
//测试
animalShout(()-> System.out.println("喵喵...调用无参、无返回值的函数式接口"));
showSum(1,2, (x, y) -> { return x + y; });
showSum(3,4, (x, y) -> x+y);
}
//创建动物叫方法,并传入接口对象作为参数
private static void animalShout(Animal animal) {
animal.shout();
}
//创建一个求和方法,并传入两个int类型参数,和接口对象
private static void showSum(int a, int b, Calculate calculate){
int res = calculate.sum(a, b);
System.out.println(a + "+" + b + "=" + res);
}
}
方法引用与构造器
类名引用静态方法
// 定义一个函数式接口
@FunctionalInterface
interface Calcable {
int calc(int num);
}
// 定义一个类,并在其中定义一个静态方法
class Math {
// 求绝对值
public static int abs(int num) {
return num > 0 ? num:-num;
}
}
public class Example22 {
private static void printAbs(int num, Calcable cal) {
System.out.println(cal.calc(num));
}
public static void main(String[] args) {
// Lambda表达式
printAbs(-10, (n)->Math.abs(n));
// 方法引用
printAbs(-10, Math::abs);
}
}
对象名引用方法
通过实例化对象的名称来对其方法进行的引用。
@FunctionalInterface
interface Printable {
void print(String str);
}
class StringUtils {
public void printUpperCase(String str) {
System.out.println(str.toUpperCase());
}
}
public class Exam23 {
public static void main(String[] args) {
StringUtils ut = new StringUtils();
// Lambda
printUpper("yyds", text-> ut.printUpperCase(text));
// 对象名引用
printUpper("yyds", ut::printUpperCase);
}
private static void printUpper(String text, Printable pt) {
pt.print(text);
}
}
构造器引用方法
构造器引用的是对类自带的构造器的引用。
@FunctionalInterface
interface PersonBuilder {
Person buildPerson(String name);
}
class Person {
private String name;
public Person(String name) {
this.name = name;
}
public String getName() { return name; }
}
public class Exam24 {
public static void main(String[] args) {
// Lambda
printName("James", name -> new Person(name));
// 构造器引用方式
printName("James", Person::new);
}
private static void printName(String name, PersonBuilder pb) {
System.out.println(pb.buildPerson(name).getName());
}
}
类名引用普通方法
通过一个普通类的类名来对其普通方法进行的引用。
@FunctionalInterface
interface Printable {
void print(StringUtils su, String str);
}
class StringUtils {
public void printUpperCase(String str) {
System.out.println(str.toUpperCase());
}
}
public class Exam25 {
public static void main(String[] args) {
// Lambda
printUpper(new StringUtils(), "yyds", (object, str)->object.printUpperCase(str));
// 方法引用
printUpper(new StringUtils(), "yyds", StringUtils::printUpperCase);
}
private static void printUpper(StringUtils su, String str, Printable pt){
pt.print(su, str);
}
}
小结
学习了面向对象的特性——多态、了解了内部类的各种种类以及对Lambda表达式的简单入门。
参考书籍:Java基础入门第二版