面向对象介绍 面向对象:并不是一个技术,而是一种编程的指导思想
以什么形式组织代码,以什么思路解决问题
为什么要学习面向对象?
因为生活中,我们要解决问题时,就是采用这种指导思想去解决的
所以,我们写程序去解决问题时,如果也能采用这种指导思想
就会使编程变得非常简单,程序也便于理解
面向对象,重点学什么?
学习自己如何设计对象
学习已有的对象如何使用
总结:面向对象是一种思想,这种思想可以让我们写代码的思路更贴切于生活
类和对象
Java中想要创建对象,必须先要有类的存在
类指的是一组相关属性和行为的集合,可以将其理解为是一张对象的设计图
Java中需要根据类,创建对象
一个类,可以创建出多个对象
类的组成 类的组成有两部分:属性和行为
属性又称成员变量 :跟之前定义变量 的格式一样,只不过位置需要放在方法外面。它用于描述事物的名词
行为又称成员方法 :跟之前定义方法 的格式一样,只不过需要去掉static关键字。它用于描述事物的动词
创建和使用对象
创建对象格式:类名 对象名 = new 类名();
成员变量的使用格式:对象名.变量名;
成员方法的使用格式:对象名.方法名(实际参数);
完整代码如下图所示:
类和对象的内存图 单个对象
多个对象
在一次程序的执行中,一个类创建了多个对象 ,也只会在方法存放一个 对应的字节码
每创建一个新的对象,就会在堆内存中开辟一个新的内存空间
虽然成员变量是独立的,但是成员方法都是引用同一份 的
引用指向内存图
成员变量和局部变量的区别
区别
成员变量
局部变量
类中位置不同
方法外
方法中
初始化值不同
有默认初始化值
没有,使用之前需要完成赋值
内存位置不同
堆内存
栈内存
生命周期不同
随着对象的创建而存在,随着对象的消失而消失
随着方法的调用而存在,随着方法的运行结束而消失
作用域
在自己所归属的大括号中
在自己所归属的大括号中
this关键字 当局部变量和成员变量出现了重名的情况,Java使用的是就近原则
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class Student { String name; int age; public void sayHello (String name) { System.out.println(name); } public void showAge (int age) { System.out.println(this .age); } } public class Test { public static void main (String[] args) { Student s = new Student (); s.name = "张三" ; s.age = 23 ; s.sayHello("李四" ); s.showAge(25 ); } }
this的本质
this代表当前类对象 的引用(地址)
哪一个对象调用的方法,方法中的this就代表哪一个对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class Student { String name; int age; public void print () { System.out.println(this ); } } public class Test { public static void main (String[] args) { Student s1 = new Student (); System.out.println(s1); s1.print(); Student s2 = new Student (); System.out.println(s2); s2.print(); } }
this内存图
构造方法
构造器:初始化一个新建的对象,构建、创造对象的时候,所调用的方法
格式:
方法名与类名相同,大小写也要一致
没有返回值类型,连void都没有
没有具体的返回值(不能由return带回结果数据)
作用:
本质作用:创建对象
结合构造方法的执行时机:给对象中 的属性(成员变量 )进行初始化
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class Student { String name; int age; public Student () { System.out.println("空参构造方法被调用了" ) } public Student (String name,int age) { this .name = name; this .age = age; System.out.println("有参构造方法被调用了" ) } } public class Test { public static void main (String[] args) { Student s1 = new Student (); Student s2 = new Student ("张三" , 23 ); } }
构造方法不能手动调用,如:s1.Student("李四", 25);是错误的
如果类中没有给出构造方法,系统会生成一个默认的无参构造方法
如果定义了构造方法,那么系统就不再会提供默认的构造方法
构造方法也是方法,运行重载关系出现
推荐使用方法:无参构造、有参构造都由用户手动定义
构造方法内存图如下:
封装 封装就是将数据(属性) 和操作数据的方法 捆绑在一起
组成一个整体,也就是类
同时,通过访问修饰符 来控制对类中属性和方法的访问权限 ,从而实现信息隐藏
封装的好处:
更好的维护数据
使用者无需关心内部实现,只要知道如何使用即可
权限修饰符
权限修饰符
同一个类中
同一个包中
不同的子类
不同包的无关类
private
✓
(default)
✓
✓
protected
✓
✓
✓
public
✓
✓
✓
✓
标准JavaBean 标准JavaBean的要求如下:
这类中的成员变量 都要私有,并且对外提供Getter和Setter方法
类中提供无参、有参构造方法
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public class Teacher { private String name; private int age; public Teacher () { } public Teacher (String name, int age) { this .name = name; this .age = age; } public String getName () { return name; } public void setName (String name) { this .name = name; } public int getAge () { return age; } public void setAge (int age) { this .age = age; } public void show () { System.out.println(name + "老师的年龄是" + age); } }
Getter方法用于获取成员属性,Setter方法用于修改成员属性
Setter方法可以对输入的数据进行处理,保证数据的安全性
JavaBean又叫做实体类,实体类只负责数据的存取,而对数据的处理交给其他类来完成,以实现数据 和数据业务 处理相分离
static关键字
static是静态 的意思,可以修饰成员变量,也可以修饰成员方法
被其修饰的成员,被该类的所有对象所共享
多了一种调用方式,可以通过类名调用 。如:Student.xxx或Student.xxx()
随着类的加载而加载 ,优先于对象存在
static修饰成员属性 static修饰成员属性的适用场景:共享数据
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 public class Student { static String school; private String name; private int age; public Student () { } public Student (String name, int age) { this .name = name; this .age = age; } public String getName () { return name; } public void setName (String name) { this .name = name; } public int getAge () { return age; } public void setAge (int age) { this .age = age; } } public class Test { public static void main (String[] args) { Student.school = "苏州大学" ; Student s1 = new Student ("张三" ,23 ); Student s2 = new Student ("李四" , 22 ); System.out.println(s1.getName() + "---" + s1.getAge() + "---" + Student.school); System.out.println(s2.getName() + "---" + s2.getAge() + "---" + Student.school); } }
static内存图 注意:随着类的加载而加载 ,优先于对象存在
static修饰成员方法 static修饰成员方法的适用场景:工具类 的制作
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 public class ArrayTools { public static int getMaxNum (int [] arr) { int max = arr[0 ]; for (int i = 1 ; i < arr.length; i++) { if (max < arr[i]) { max = arr[i]; } } return max; } public static int getMinNum (int [] arr) { int min = arr[0 ]; for (int i = 1 ; i < arr.length; i++) { if (min > arr[i]) { min = arr[i]; } } return min; } public static void printArray (int [] arr) { for (int n : arr) { System.out.print(n + "," ); } System.out.println(); } } import java.util.Random;public class Test { public static void main (String[] args) { Random r = new Random (); int [] arr = new int [10 ]; for (int i = 0 ; i < arr.length; i++) { arr[i] = r.nextInt(-100 ,100 ); } ArrayTools.printArray(arr); System.out.println("数组最大元素为:" + ArrayTools.getMaxNum(arr)); System.out.println("数组最小元素为:" + ArrayTools.getMinNum(arr)); } }
上面的工具类实现了基础的功能,但还有优化的地方,优化后的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 package com.norlcyan._static;public class ArrayTools { private ArrayTools () {}; public static int getMaxNum (int [] arr) { int max = arr[0 ]; for (int i = 1 ; i < arr.length; i++) { if (max < arr[i]) { max = arr[i]; } } return max; } public static int getMinNum (int [] arr) { int min = arr[0 ]; for (int i = 1 ; i < arr.length; i++) { if (min > arr[i]) { min = arr[i]; } } return min; } public static void printArray (int [] arr) { for (int n : arr) { System.out.print(n + "," ); } System.out.println(); } }
优化点1:私有化了构造方法,防止用户创建对象(因为该类中都为static方法,不需要对象就可以调用)
优化点2:使用文档注释,让工具类更加规范、易读
static注意事项
static中只能访问静态成员(直接访问)
1 2 3 4 5 6 7 8 9 10 public class Test { int a = 10 ; static int b = 20 ; public static void main (String[] args) { System.out.println(a); System.out.println(b); } }
static中没有this关键字
1 2 3 4 5 public class Test { public static void main (String[] args) { System.out.println(this ); } }
重新认识main方法 1 2 3 4 5 6 7 8 9 package com.norlcyan._static;import java.util.Random;public class Test { public static void main (String[] args) { System.out.println("Hello World" ); } }
public:被JVM调用,访问权限足够大
static:被JVM调用,不用创建对象。因为main方法是静态的,所以测试类中其他方法也需要是静态的
void:被JVM调用,不需要给JVM返回值
main:一个通用的名称,虽然不是关键字,但是被JVM识别
String[] args:以前用于接收键盘录入数据的,现在不再使用,但作为传统保留下来了
文档注释使用 通过IDEA中的 Tools → Generate JavaDoc 生成
继承
继承:让类与类之间产生关系(子父类关系 ),子类可以直接使用父类中非私有成员
使用场景:当类与类之间,存在相同(共性)的内容,并且产生了is a的关系,就可以考虑使用继承,来优化代码
继承的优势:提高代码复用性,维护性
继承的格式:
格式:public class 子类名 extends 父类名 { }
范例:public class Zi extends Fu { }
Fu:父类,也称为基类、超类
Zi:子类,也称为派生类
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 package com.norlcyan._extends;public class ExtendsDemo01 { public static void main (String[] args) { Coder coder = new Coder (); coder.setName("张三" ); coder.setAge(23 ); coder.setSalary(6666.6 ); System.out.println(coder.getName() + "---" + coder.getAge() + "---" + coder.getSalary()); } } class Employee { private String name; private int age; private double salary; public String getName () { return name; } public void setName (String name) { this .name = name; } public int getAge () { return age; } public void setAge (int age) { this .age = age; } public double getSalary () { return salary; } public void setSalary (double salary) { this .salary = salary; } } class Coder extends Employee {}
一个文件中是可以存在多个类的,但是只能有一个公共类
Super关键字 当子父类中,出现了重名的成员变量,使用的时候依旧是就近原则
如果子类需要使用父类中的重名变量,可以使用super关键字
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package com.norlcyan._extends;public class ExtendsDemo02 { public static void main (String[] args) { Zi z = new Zi (); z.show(); } } class Fu { int num = 10 ; } class Zi extends Fu { int num = 30 ; public void show () { System.out.println("不使用Super:" + num); System.out.println("使用Super:" + super .num); } }
重写成员方法
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package com.norlcyan._extends;public class ExtendsDemo03 { public static void main (String[] args) { Son s = new Son (); s.saySomething(); } } class Father { public void saySomething () { System.out.println("I am Father" ); } public void sayHello () { System.out.println("Hello World" ); } } class Son extends Father { @Override public void saySomething () { super .sayHello(); System.out.println("I am Son" ); } }
Java中继承的特点
Java只支持单继承 ,不支持多继承 ,但支持多层继承
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public class ExtendsDemo04 { public static void main (String[] args) { C c = new C (); c.methodA(); c.methodB(); c.methodC(); } } class A { public void methodA () { System.out.println("I am A's Method" ); } } class B extends A { public void methodB () { System.out.println("I am B's Method" ); } } class C extends B { public void methodC () { System.out.println("I am C's Method" ); } } class D extends C ,B,A { ... }
构造方法
子类在初始化之前,需要先完成父类的初始化
系统会在子类的构造方法自动调用父类的构造方法,在子类所有的构造方法中,第一句话默认隐藏了super();访问父类的空参构造方法 ,从而完成父类的初始化操作
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 package com.norlcyan._extends;public class ExtendsDemo05 { public static void main (String[] args) { _Zi _zi1 = new _Zi (); System.out.println("--------------------" ); _Zi _zi2 = new _Zi (10 ); } } class _Fu { public _Fu () { System.out.println("Fu 空参构造方法被调用了" ); } public _Fu (int num) { System.out.println("Fu 带参构造方法被调用了" ); } } class _Zi extends _Fu { public _Zi () { System.out.println("Zi 空参构造方法被调用了" ); } public _Zi (int num) { System.out.println("Zi 带参构造方法被调用了" ); } }
构造方法内存图
完整案例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 package com.norlcyan._extends;public class ExtendsDemo06 { public static void main (String[] args) { Teacher teacher = new Teacher ("张三" , 30 ,10001 ); System.out.println(teacher.getTeacherID() + "---" + teacher.getName() + "---" + teacher.getAge()); Student student = new Student (); student.setName("李四" ); student.setAge(18 ); student.setScore(99.5 ); System.out.println(student.getName() + "---" + student.getAge() + "---" + student.getScore()); } } class Person { private String name; private int age; public Person () { } public Person (String name, int age) { this .name = name; this .age = age; } public String getName () { return name; } public void setName (String name) { this .name = name; } public int getAge () { return age; } public void setAge (int age) { this .age = age; } } class Teacher extends Person { private int teacherID; public Teacher () { } public Teacher (int teacherID) { this .teacherID = teacherID; } public Teacher (String name, int age, int teacherID) { super (name, age); this .teacherID = teacherID; } public int getTeacherID () { return teacherID; } public void setTeacherID (int teacherID) { this .teacherID = teacherID; } } class Student extends Person { private double score; public Student () { } public Student (double score) { this .score = score; } public Student (String name, int age, double score) { super (name, age); this .score = score; } public double getScore () { return score; } public void setScore (double score) { this .score = score; } }
继承重要案例 1 2 3 4 程序员类 Coder: 成员变量:姓名,年龄,工资 成员方法:work方法,实现内容如下所示↓ 姓名为张三,年龄为23,工资为15000的程序员正在编写代码
1 2 3 4 项目经理类 Manage: 成员变量:姓名,年龄,工资,奖金 成员方法:work方法,实现内容如下所示↓ 姓名为李四,年龄为24,工资为18000,奖金为5000的项目经理正在分配任务...
1 2 3 员工类 Employee: 成员变量:姓名,年龄,工资 成员方法:work方法
私有成员变量
提供空参、有参构造方法
提供Getter和Setter
编写work方法
Employee:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 package com.norlcyan.extends_practice;public class Employee { private String name; private int age; private double salary; public Employee () { } public Employee (String name, int age, double salary) { this .name = name; this .age = age; this .salary = salary; } public String getName () { return name; } public void setName (String name) { this .name = name; } public int getAge () { return age; } public void setAge (int age) { this .age = age; } public double getSalary () { return salary; } public void setSalary (double salary) { this .salary = salary; } public void work () {} }
Manager:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 package com.norlcyan.extends_practice;public class Manager extends Employee { private double bonus; public Manager () { } public Manager (String name, int age, double salary, double bonus) { super (name, age, salary); this .bonus = bonus; } public double getBonus () { return bonus; } public void setBonus (double bonus) { this .bonus = bonus; } @Override public void work () { System.out.println("姓名为" + super .getName() + ",年龄为" + super .getAge() + ",工资为" + super .getSalary() + ",奖金为" + this .bonus + "的项目经理正在分配任务..." ); } }
Coder:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package com.norlcyan.extends_practice;public class Coder extends Employee { public Coder () { } public Coder (String name, int age, double salary) { super (name, age, salary); } @Override public void work () { System.out.println("姓名为" + super .getName() + ",年龄为" + super .getAge() + ",工资为" + super .getSalary() + "的程序员正在编写代码" ); } }
测试类:
1 2 3 4 5 6 7 8 9 10 11 package com.norlcyan.extends_practice;public class Test { public static void main (String[] args) { Coder coder = new Coder ("张三" ,23 ,15000 ); coder.work(); Manager manager = new Manager ("李四" , 24 , 18000 , 5000 ); manager.work(); } }
this和super的区别
this:代表本类对象的引用 (内存地址)
super:代表父类存储空间的标识
关键字
访问成员变量
访问成员方法
访问构造方法
this
this.本类成员变量;
this.本类成员方法();
必须在构造方法第一行
super
super.父类成员变量;
super.父类成员方法();
必须在构造方法第一行
如果调用的成员在子类中不存在,可以省略super不写
this()、super()二者都在争夺构造方法的第一行,所以二者不能共存
this调用构造方法示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 package com.norlcyan.thisAndSuper;public class Demo01 { public static void main (String[] args) { A a1 = new A (1 ,2 ); A a2 = new A (3 ,4 ,5 ); } } class A { int a; int b; int c; public A () { } public A (int a, int b) { this .a = a; this .b = b; } public A (int a, int b, int c) { this (a,b); this .c = c; } }
final关键字 final关键字是最终的意思,可以修饰方法、类、变量
final修饰的特点:
修饰方法:表明该方法是最终方法,不能被重写
修饰类:表明该类是最终类,不能被继承
修饰变量:表明该变量是常量,不能再次被赋值
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package com.norlcyan._final;public class FinalTest { public static void main (String[] args) { final int NUM = 10 ; System.out.println(NUM); final int [] arr1 = {1 ,2 ,3 ,4 ,5 }; int [] arr2 = {10 ,20 ,30 ,40 ,50 }; arr1[0 ] = 11 ; System.out.println(arr1[0 ]); } } class Fu { public final void privacyMethod () { System.out.println("隐私方法,不能被随意修改" ); } } final class Zi extends Fu { }
final修饰基本数据类型时,数据值 不能改变
final修饰引用数据类型时,地址值 不能改变,但地址里面的内容是可以改变的
final修饰成员变量时,要么在定义的时候完成赋值,要么在构造方法之前完成赋值(也就是不存在默认值)。如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 package com.norlcyan._final;public class FinalTest {} class A { final int num = 10 ; } class B { final int num; public B (int num) { this .num = num; } } class C { final int num; public C () {}; public C (int num) { this .num = num; } }
常量命名规范:如果是一个单词,所有字母大写。如果是多个单词,所有字母大写,单词之间用”_”分隔
抽象类
抽象类是一种特殊的父类,内部可以编写抽象方法
抽象方法:将共性的行为(方法)抽取到父类之后,发现该方法的实现逻辑无法在父类中给出具体明确 ,该方法就可以定义为抽象方法
特点:当父类存在抽象方法,那么子类必须要将抽象方法重写 ,给出具体实现内容
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 package com.norlcyan.AbstractClass;public class AbstractDemo01 { public static void main (String[] args) { Dog dog = new Dog (); dog.eat(); } } abstract class Animal { public abstract void eat () ; } class Dog extends Animal { @Override public void eat () { System.out.println("狗吃肉" ); } } class Cat extends Animal { @Override public void eat () { System.out.println("猫吃鱼" ); } }
注意事项:
抽象类不能实例化
抽象类存在构造方法
抽象类中可以存在普通方法
抽象类的子类
抽象类中的方法不一定都是抽象方法,但有抽象方法的类一定是抽象类
abstract关键字冲突问题
final:被abstract修饰的方法,强制要求子类重写,但被final修饰的方法子类不能重写
private:被abstract修饰的方法,强制要求子类重写,但被private修饰的方法不能重写
static:被static修饰的方法可以类名调用,类名调用抽象方法没有意义
模板设计模式
设计模式是软件开发中针对反复出现问题总结归纳出来的通用解决方案
模板设计模式:把抽象类整体就可以看成一个模板,模板中不能决定的东西定义成抽象方法,让使用模板的类(继承抽象类的类)取重写抽象方法实现需求
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package com.norlcyan.template;public class Test { public static void main (String[] args) { Book b = new Book (); b.write(); } } abstract class TextTemplate { public final void write () { System.out.println("文章标题..." ); body(); System.out.println("文章结尾..." ); } public abstract void body () ; } class Book extends TextTemplate { @Override public void body () { System.out.println("这是文章的主题内容..." ); } }
接口
接口:体现的思想是对规则的声明。Java中的接口更多体现的是对行为的抽象
格式:public interface 接口名 {}
接口不能实例化
接口和类之间是实现关系 ,通过implements关键字表示:public class 类名 implements 接口名 {}
接口的子类(实现类 )要么重写接口中的所有方法,要么是抽象类
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package _interface;public class InterfaceDemo01 { public static void main (String[] args) { InterImpl ii = new InterImpl (); ii.method(); ii.show(); } } interface inter { public abstract void show () ; public abstract void method () ; } class InterImpl implements inter { @Override public void show () { System.out.println("show" ); } @Override public void method () { System.out.println("method" ); } } abstract class InterImpl2 implements inter {}
接口中的成员特点 成员变量特点:只能是常量 ,因为默认加入三个关键字:public static final,即使不手动添加也会存在
成员方法特点:只能是抽象方法 ,因为默认加入两个关键字:public abstract,即使不手动添加也会存在
构造方法:没有
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package _interface;public class InterfaceDemo02 { public static void main (String[] args) { System.out.println(MyInter.num); MyInterImpl mii = new MyInterImpl (); mii.show(); } } interface MyInter { int num = 10 ; void show () ; } class MyInterImpl implements MyInter { public MyInterImpl () { super (); } @Override public void show () { System.out.println("..." ); } }
但是,在JDK8和JDK9中针对成员方法,引入了部分新特性,详细内容在后续章节会提到
类和接口之间的各种关系
类和类的关系:继承关系,只能单继承 ,但是可以多层继承
类和接口的关系:实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口。接口可以看做是一种特殊的抽象父类
接口和接口之间的关系:继承关系,可以单继承,也可以多继承
类和接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 package _interface;public class InterfaceDemo03 {} interface InterA { void showA () ; void test () ; } interface InterB { void showB () ; void test () ; } class Fu { public void test () { System.out.println("Extends Test" ); } } class InterABImpl extends Fu implements InterA ,InterB { @Override public void showA () { System.out.println("Show A Method" ); } @Override public void showB () { System.out.println("Show B Method" ); } }
如果接口A和接口B有重名的方法,实现类中只需要重写一次即可(因为接口只是定义规则,不实现具体逻辑,所以不存在冲突 )
如果接口A、接口B、父类Fu有重名的方法,当实现类继承了Fu,那在实现类中不重写重名方法也没问题,就相当于父类完成了接口中方法的重写
接口和接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 package _interface;public class InterfaceDemo03 {} interface A { void methodA () ; void test () ; } interface B { void methodB () ; void test () ; } interface C extends A ,B { void methodC () ; void test () ; } class CImpl implements C { @Override public void methodC () { } @Override public void methodA () { } @Override public void methodB () { } @Override public void test () { } }
接口之间可以多继承,但是实现类要将继承的所有接口中的方法都实现
接口与接口继承时,即使出现重名方法也不会报错,只需要实现类中实现一次 即可
抽象类和接口的对比
成员变量:
抽象类:可以定义变量,也可以定义常量
接口:只能定义常量
成员方法:
抽象类:可以是定义具体方法,也可以定义抽象方法
接口:只能定义抽象方法
构造方法:
抽象类:抽象类的本质还是在描述一个事物 ,只是有部分的方法无法直接说明,所以需要抽象方法
接口:接口可以为程序制定规则 ,代码更加规范。实现类实现接口中的方法,方法名不能修改,方法数量不能缺少 。这样当后期维护、交接代码的时候,可以更加快速的上手。
接口新特性(接口升级问题) JDK8的新特性:接口中可以定义有方法体 的方法(修饰符必须为默认修饰符default 或静态修饰符static )
JDK9的新特性:接口中可以定义私有方法 (修饰符为私有修饰符private )
1 2 3 interface Inter {}class AInterImpl implements Inter {}class BInterImpl implements Inter {}
假设最开始设计Inter接口时,接口中只有5个方法。但后期对接口进行丰富后,方法变为了10个,那么实现类就会全部报错。
允许在接口中定义非抽象方法,但是需要使用关键字default修饰,这些方法就是默认方法
作用:解决接口升级问题
default 接口中默认方法的定义格式:
格式:public default 返回值类型 方法名(参数列表) {}
示例:public default void show() {}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 package _interface;public class InterfaceDemo04 { public static void main (String[] args) { OrderServiceImpl o = new OrderServiceImpl (); o.create(); o.cancel(); o.paid(); o.delete(); } } interface OrderService { void create () ; void cancel () ; default void paid () { System.out.println("支付订单" ); } default void delete () { System.out.println("删除订单" ); } } class OrderServiceImpl implements OrderService { @Override public void create () { System.out.println("创建订单" ); } @Override public void cancel () { System.out.println("取消订单" ); } }
但是,由于在接口中定义了方法的具体实现逻辑,那就会导致多实现出问题(因为多实现中,方法不会给出具体实现逻辑,所以不会引起重名方法冲突 问题)
解决方法就是在实现类中,重写重名的方法,可以选择根据哪个接口的方法进行继承,或者自己重写全部实现逻辑:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 package _interface;public class InterfaceDemo05 {} interface TestA { default void show () { System.out.println("default Show A Method" ); } default void test () { System.out.println("default Test A Method" ); } } interface TestB { default void show () { System.out.println("default Show B Method" ); } default void test () { System.out.println("default Test BMethod" ); } } class TestABImpl implements TestA , TestB { @Override public void show () { TestA.super .show(); } @Override public void test () { System.out.println("My Test Method" ); } }
默认方法不是抽象方法,所以不强制被重写(但是可以被重写,重写的时候去掉default关键字)
public可以省略,default不能省略
如果实现了多个接口,多个接口中存在相同的方法声明,子类就必须对该方法进行重写
static 接口中允许定义static静态方法
接口中静态方法的定义格式:
格式:public static 返回值类型 方法名(参数列表) {}
示例:public static void show() {}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package _interface;public class InterfaceDemo06 { public static void main (String[] args) { P p = new P (); p.eat(); StaticInter.show(); } } interface StaticInter { static void show () { System.out.println("Interface Static Show Method" ); } void eat () ; } class P implements StaticInter { @Override public void eat () { System.out.println("吃饭" ); } }
静态方法只能通过接口名调用 ,不能通过实现类名或者对象名调用
public可以省略,static不能省略
private 接口中允许定义private私有方法
接口中私有方法的定义格式:
格式1:private 返回值类型 方法名(参数列表) {}
示例:private void show() {}
格式2:private static 返回值类型 方法名(参数列表) {}
示例:private static void method() {}
使用场景:当接口中的某个方法只希望被自己内部的默认方法或静态方法调用时,而外部不能允许调用时,就可以用private修饰符修饰
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 package _interface;public class InterfaceDemo07 { public static void main (String[] args) { Logger l = new Logger ("日志记录器A" ); l.start(); l.end(); } } interface MyLogger { default void start () { System.out.println("start方法执行..." ); log(); } default void end () { System.out.println("end方法执行..." ); log(); } private void log () { System.out.println("日志记录" ); } } class Logger implements MyLogger { private String name; public Logger (String name) { this .name = name; } public String getName () { return name; } public void setName (String name) { this .name = name; } }
多态 同一个行为具有多个不同表现形式或形态的能力
对象多态:将方法的形参定义为父类类型 ,这个方法可以接收父类的任意子类对象
public static void useAnimal(Animal a);
假设有个Cat类和Dog类继承了Animal类,那么调用方法的时候就可以useAnimal(new Dog())或useAnimal(new Cat())
更详细的案例代码可以看多态的好处和弊端这一章
行为多态:同一个行为,具有多个不同表现形式或形态的能
还是上面的案例,当方法体中调用了a.eat(),但是根据传入的不同对象能够呈现出不同的形式,这个就是行为多态
多态前提
有继承/实现关系
有方法重写
有父类引用指向子类对象
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package polymorphism;public class PolymorphismDemo01 { public static void main (String[] args) { Zi z = new Zi (); System.out.println(z.num); z.show(); Fu f = new Zi (); System.out.println(f.num); f.show(); } } class Fu { int num = 10 ; public void show () { System.out.println("Fu...Show..." ); } } class Zi extends Fu { int num = 20 ; @Override public void show () { System.out.println("Zi...Show..." ); } }
成员变量:**编译看左边(父类),运行看左边(父类) **
因为是父类的引用,所以访问存在局限性 ,只能访问super空间中的数据
成员方法:**编译看左边(父类),运行看右边(子类) **
编译时检查方法在父类中是否存在
不存在:编译出错
存在:编译通过,但运行的时候 ,一定会执行子类的方法逻辑 。这是因为如果父类是抽象方法,那么调用父类中的方法没有意义,所以为了确保不出现这种情况,就将其设计为了执行子类方法
静态成员:**编译看左边(父类),运行看左边(父类) **
static修饰的成员,推荐使用类名调用
假设show是静态方法,那么f.show(); ===> 字节码文件中 ===> Fu.show();
成员变量内存图:
多态的好处和弊端
如果对多态不了解,一定要看懂以下示例代码 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 package polymorphism;public class PolymorphismDemo02 { public static void main (String[] args) { useDog(new Dog ()); useCat(new Cat ()); useAnimal(new Dog ()); useAnimal(new Cat ()); } public static void useDog (Dog d) { d.eat(); } public static void useCat (Cat c) { c.eat(); } public static void useAnimal (Animal a) { a.eat(); } } abstract class Animal { public abstract void eat () ; } class Dog extends Animal { @Override public void eat () { System.out.println("狗吃肉" ); } public void watchHome () { System.out.println("狗看家" ); } } class Cat extends Animal { @Override public void eat () { System.out.println("猫吃鱼" ); } public void catchMouse () { System.out.println("猫抓老鼠" ); } }
方法的形参定义为父类类型,就可以传入该类的任意子类对象 了,但方法中只能调用父类与子类的共性方法
多态转型
多态的弊端:不能使用子类的特有成员
向上转型:
从子到父(父类引用指向子类对象)
Fu f = new Zi();
向下转型:
从父到子(父类引用所指向的对象,交给子类类型)
当父类对象需要访问子类特有的方法 或子类的属性时 ,需要进行向下转型
Zi z = (Zi)f;
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 package polymorphism;public class PolymorphismDemo02 { public static void main (String[] args) { useAnimal(new Dog ()); useAnimal(new Cat ()); } public static void useAnimal (Animal a) { a.eat(); if (a instanceof Dog) { Dog d = (Dog) a; d.watchHome(); } else if (a instanceof Cat) { Cat c = (Cat) a; c.catchMouse(); } } } abstract class Animal { public abstract void eat () ; } class Dog extends Animal { @Override public void eat () { System.out.println("狗吃肉" ); } public void watchHome () { System.out.println("狗看家" ); } } class Cat extends Animal { @Override public void eat () { System.out.println("猫吃鱼" ); } public void catchMouse () { System.out.println("猫抓老鼠" ); } }
如果被转的引用类型变量,对应的实际类型和目标类型不是同一种类型,那么在转换的时候就会出现ClassCastException
1 2 Animal a = new Dog ();Cat c = (Cat) a;
可以使用instanceof 关键字判断,格式:对象名 instanceof 类型
判断一个对象是否是一个类的实例,通俗理解就是判断关键字左边的对象 是否是右边的类型 ,返回布尔类型结果
instanceof不能判断基本数据类型
综合案例 订单业务案例 订单业务接口代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 package order;public interface OrderService { void create () ; void findOne () ; void findList () ; void cancel () ; void finish () ; void paid () ; }
订单业务实现类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 package order;public class OrderServiceImpl implements OrderService { @Override public void create () { System.out.println("创建单个订单" ); } @Override public void findOne () { System.out.println("查询单个订单" ); } @Override public void findList () { System.out.println("查询订单列表" ); } @Override public void cancel () { System.out.println("取消订单" ); } @Override public void finish () { System.out.println("完结订单" ); } @Override public void paid () { System.out.println("支付订单" ); } }
海外订单业务实现类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 package order;public class OverseasOrderServiceImpl implements OrderService { public void check () { System.out.println("IP地址检测" ); } @Override public void create () { System.out.println("国外业务---创建单个订单" ); } @Override public void findOne () { System.out.println("国外业务---查询单个订单" ); } @Override public void findList () { System.out.println("国外业务---查询订单列表" ); } @Override public void cancel () { System.out.println("国外业务---取消订单" ); } @Override public void finish () { System.out.println("国外业务---完结订单" ); } @Override public void paid () { System.out.println("国外业务---支付订单" ); } }
测试类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 package order;import java.util.Scanner;public class Test { public static void main (String[] args) { Scanner sc = new Scanner (System.in); System.out.println("请选择业务范围:1. 国内业务 2. 国外业务" ); int n = sc.nextInt(); OrderService orderService = null ; switch (n) { case 1 : orderService = new OrderServiceImpl (); break ; case 2 : orderService = new OverseasOrderServiceImpl (); OverseasOrderServiceImpl overseasOrderService = (OverseasOrderServiceImpl) orderService; overseasOrderService.check(); break ; } if (orderService != null ) { orderService.create(); orderService.findOne(); orderService.findList(); orderService.cancel(); orderService.finish(); orderService.paid(); } } }
结构图:
模拟支付接口 需求:某网站需要开发一个支付功能,需要支持多种支付方法(支付平台支付、银行卡网银支付、信用卡快捷支付)
支付过程如下:
1 2 3 4 请选择支付方式:1、支付平台支付 2、银行卡网银支付 3、信用卡快捷支付 请输入您的支付方式:2 请输入您的支付金额:56.78 通过银行卡网银支付了:56.78元!
支付接口:
1 2 3 4 5 package payment;public interface PaymentService { void paid (double money) ; }
支付平台支付实现类:
1 2 3 4 5 6 7 8 package payment;public class PlatformPaymentServiceImpl implements PaymentService { @Override public void paid (double money) { System.out.println("通过支付平台支付了:" + money + "元" ); } }
银行卡网银支付:
1 2 3 4 5 6 7 8 9 package payment;public class BankcardPaymentServiceImpl implements PaymentService { @Override public void paid (double money) { System.out.println("通过银行卡网银支付了:" + money + "元" ); } }
信用卡快捷支付:
1 2 3 4 5 6 7 8 9 package payment;public class CreditcardPaymentServiceImpl implements PaymentService { @Override public void paid (double money) { System.out.println("通过信用卡快捷支付了:" + money + "元" ); } }
测试类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package payment;import java.util.Scanner;public class Test { public static void main (String[] args) { Scanner sc = new Scanner (System.in); System.out.println("请选择支付方式:1、支付平台支付 2、银行卡网银支付 3、信用卡快捷支付" ); System.out.print("请输入您的支付方式:" ); int choice = sc.nextInt(); PaymentService paymentService = switch (choice) { case 1 -> new PlatformPaymentServiceImpl (); case 2 -> new BankcardPaymentServiceImpl (); case 3 -> new CreditcardPaymentServiceImpl (); default -> null ; }; if (paymentService != null ) { System.out.print("请输入您的支付金额:" ); double money = sc.nextDouble(); paymentService.paid(money); } } }
结构图:
Object类
所有的类,都直接或者间接的继承了Object类(祖宗类)
Object类的方法是一切子类都可以直接使用 的,所以学习Object是很有必要的
Object类的常用方法:
方法名
说明
public String toString()
默认是返回当前对象在堆内存中的地址信息:类的全限名@内存地址
public boolean equals(Object o)
比较两个对象是否相同
toString()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 package com.norlcyan.MyObject;public class ObjectDemo { public static void main (String[] args) { Student student = new Student ("张三" ,18 ); System.out.println(student.toString()); System.out.println(student); } } class Student { private String name; private int age; public Student () { } public Student (String name, int age) { this .name = name; this .age = age; } public String getName () { return name; } public void setName (String name) { this .name = name; } public int getAge () { return age; } public void setAge (int age) { this .age = age; } @Override public String toString () { return "Student{" + "name='" + name + '\'' + ", age=" + age + '}' ; } }
开发中直接输出对象,默认输出对象的地址其实是毫无意义的
开发中输出对象变量,更多的时候是希望看到对象的内容数据而不是对象的地址信息
equals()
细节:
当引用数据类型直接进行比较(==)时,比较的是地址值。也就说,即使两个对象中的所有属性都相同,比较的结果也是false
调用equals方法对两个对象进行比较,如果类中没有重写的话,底层依旧是比较两个对象的地址值
Object类的equals方法实现:
1 2 3 public boolean equals (Object obj) { return (this == obj); }
重写equals方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 package Pojo;import java.util.Objects;public class Student { private String name; private int age; public Student () { } public Student (String name, int age) { this .name = name; this .age = age; } public String getName () { return name; } public void setName (String name) { this .name = name; } public int getAge () { return age; } public void setAge (int age) { this .age = age; } @Override public boolean equals (Object obj) { if (!(obj instanceof Student)) { return false ; } Student sObj = (Student) obj; return this .age == sObj.age && this .name.equals(sObj.name); } }
注意:return this.age == obj.age;属于多态的弊端,父类不能访问子类的成员属性。这里Object obj = new Student();
IDEA重写equals() 代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Override public boolean equals (Object o) { if (this == o) return true ; if (o == null || getClass() != o.getClass()) return false ; Student student = (Student) o; return age == student.age && Objects.equals(name, student.name); } @Override public int hashCode () { return Objects.hash(name, age); }
Objects.equals(name, student.name),Objects类下的一个静态方法,注意不是Object而是Objects
当快速生成重写equals方法的代码时,会有一个选项提示是否生成hashCode()方法 重写,推荐生成。这是为了与哈希集合(如 HashMap、HashSet)的兼容性
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Person { String name; int age; @Override public boolean equals (Object obj) { } } Person p1 = new Person ("Alice" , 20 );Person p2 = new Person ("Alice" , 20 );System.out.println(p1.equals(p2)); System.out.println(p1.hashCode() == p2.hashCode());
此时,如果将 p1 和 p2 放入 HashSet,它们会被视为两个不同的对象,导致集合中出现重复元素 。
Objects Objects类与Object还是继承关系,Objects类是从JDK1.7开始之后才有的
Objects常见方法:
方法名
说明
public static boolean equals (Object a,Object b)
比较两个对象的,底层会先进行非空判断,从而可以避免空指针异常。再进行equals比较
public static boolean isNull (Object obj)
判断变量是否为null
Objects.euqals底层实现:
1 2 3 4 5 public static boolean equals (Object a, Object b) { return (a == b) || (a != null && a.equals(b)); }
示例:
1 2 3 4 5 6 7 8 9 10 11 12 package Pojo;import java.util.Objects;public class Test { public static void main (String[] args) { Student s1 = null ; Student s2 = new Student ("张三" , 18 ); System.out.println(s1.equals(s2)); System.out.println(Objects.equals(s1,s2)); } }
当需要进行两个对象的相同值比较时,推荐使用Objects.equals(Obj o1,Obj o2),可以避免空指针异常
代码块 在Java类下,使用{ }括起来的代码被称为代码块
分类:
局部代码块
构造代码块
静态代码块
同步代码块:在多线程一章讲解
内部类
1 2 3 4 5 class Outer { class Inner { } }
内部类分类:
内部类的优势:封装性更好,但是使用起来较为繁琐,建议少用
成员内部类
创建内部类对象格式:
格式:外部类名.内部类名 对象名 = new 外部类对象().new 内部类对象();
示例:Outer.Inner in = new Outer().new Inner();
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class InnerDemo01 { public static void main (String[] args) { Outer.Inner oi = new Outer ().new Inner (); System.out.println(oi.a); oi.show(); } } class Outer { class Inner { int a = 10 ; public void show () { System.out.println("Inner Class Show" ); } } }
成员访问特点
内部类访问外部类:所有成员都可以直接访问,包括私有成员
外部了访问内部类:需要创建内部类对象才能访问
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 package innerClass;public class InnerDemo01 { public static void main (String[] args) { Outer.Inner oi = new Outer ().new Inner (); oi.innerPrint(); Outer o = new Outer (); o.outerPrint(); } } class Outer { public int outerNum = 20 ; public void outerShow () { System.out.println("Outer Class Show" ); } public void outerPrint () { Inner i = new Inner (); System.out.println(i.innerNum); i.innerShow(); } class Inner { public int innerNum = 10 ; public void innerShow () { System.out.println("Inner Class Show" ); } public void innerPrint () { System.out.println(outerNum); outerShow(); } } }
当外部类和内部类出现了重名属性或方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package innerClass;public class InnerDemo01 { public static void main (String[] args) { Outer.Inner oi = new Outer ().new Inner (); oi.show(); } } class Outer { int num = 150 ; class Inner { int num = 110 ; public void show () { int num = 70 ; System.out.println(num); System.out.println(this .num); System.out.println(Outer.this .num); } } }
静态内部类
有static修饰的成员内部类
定义格式:
1 2 3 4 5 class Outer { static class Inner { } }
创建静态内部类对象格式:
格式:外部类名.内部类名 对象名 = new 外部类名.内部类对象();
示例:Outer.Inner in = new Outer.Inner();
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class InnerDemo01 { public static void main (String[] args) { Outer.Inner oi = new Outer .Inner(); oi.show(); Outer.Inner.method(); } } class Outer { static class Inner { public void show () { System.out.println("Inner Show" ); } public static void method () { System.out.println("Inner Method" ); } } }
局部内部类
定义在方法、代码块、构造器等执行体中的类,没啥用,了解即可
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class InnerDemo01 { public static void main (String[] args) { Outer o = new Outer (); o.show(); } } class Outer { public void show () { class B { public void print () { System.out.println("Print..." ); } } B b = new B (); b.print(); } }
匿名内部类(重点) 首先,先观察以下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package innerClass;public class InnerDemo02 { public static void main (String[] args) { useInter(new InterImpl ()); } public static void useInter (Inter i) { i.show(); } } interface Inter { void show () ; } class InterImpl implements Inter { @Override public void show () { System.out.println("实现类重写后的show方法..." ); } }
可以发现,如果方法的参数为接口,那么调用方法时还需要专门创建一个实现类 ,然后再将该类对象作为参数才能调用
概述:匿名内部类本质上是一个特殊的局部内部类(定义在方法内部)
前提:需要存在一个接口或类
格式:
new 类名() {} ==> 继承这个类,示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package innerClass;public class Test { public static void main (String[] args) { new Fu () { @Override public void show () { System.out.println("继承后的Show方法" ); } }.show(); } } class Fu { public void show () { System.out.println("Fu Show方法" ); } }
new 接口() {} ==> 实现这个接口,示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package innerClass;public class InnerDemo02 { public static void main (String[] args) { useInter(new Inter () { @Override public void show () { System.out.println("实现类重写后的show方法..." ); } }); } public static void useInter (Inter i) { i.show(); } } interface Inter { void show () ; }
当接口中只有一个方法时,推荐使用匿名内部类,但是当存在多个方法 ,还是推荐创建实现类的方式
注意:
在匿名内部类中修改外部变量时,Java 要求该变量必须是 final 或等效不可变的 在匿名内部类中修改外部变量时,Java 要求该变量必须是 final 或等效不可变的
包
包本质来说就是文件夹,用来管理类文件的
建包的语法格式:package 公司域名倒写.技术名称。包名建议全部英文小写,且具备意义
建包语句必须在第一行,一般IDEA工具会帮助创建
导包
相同包下的类可以直接方法,但是不同包下的类必须导包,才可以使用!
如果导入的类在java.lang包(核心包 ),就不需要编写import导包代码
导入具体包下的类,格式:import 包名.类名;
导入包下的所有类,格式:import 包名.*;
一个类中,需要使用不同的类,但是这两个类名是相同的
默认只能导入一个,另一个需要带包访问:
1 2 3 4 5 6 7 8 9 import java.util.*;public class PackageDemo { public static void main (String[] args) { Scanner scanner = new Scanner (); java.util.Scanner sc = new java .util.Scanner(System.in); } }
Lambda表达式
Lambda表达式是JDK8开始后的一种新语法形式
作用:简化匿名内部类的代码写法
格式:
1 2 3 4 (匿名内部类被重写方法的形参列表) -> { 被重写方法的方法体代码 } 注:-> 是语法形式,无实际意义
注意:Lambda表达式只能简化函数式接口 的匿名内部类的写法形式
函数式接口
首先必须是接口,其次接口中有且仅有一个抽象方法 的形式
通常我们会在接口上加一个@FunctionalInterface注解,标记该接口必须是满足函数式接口的
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @FunctionalInterface interface A { void showA () ; } interface B { void showB () ; } interface C { void showC () ; void test () ; }
A、B是函数式接口,C因为有一个以上的方法,所以只是普通接口
完整代码1:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package lambda;public class LambdaTest01 { public static void main (String[] args) { useShowHandler(() -> { System.out.println("Lambda表达式" ); }); } public static void useShowHandler (ShowHandler showHandler) { showHandler.show(); } } @FunctionalInterface interface ShowHandler { void show () ; }
完整代码2:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package lambda;public class LambdaTest02 { public static void main (String[] args) { useStringHandler((String msg) -> { System.out.println(msg + " Lambda" ); }); } public static void useStringHandler (StringHandler stringHandler) { stringHandler.printMessage("stringHandler" ); } } @FunctionalInterface interface StringHandler { void printMessage (String msg) ; }
完整代码3(重要):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package lambda;import java.util.Random;public class LambdaTest03 { public static void main (String[] args) { int num1 = getRandomNumber(() -> { return new Random ().nextInt(100 ) + 1 ; }); int num2 = getRandomNumber(() -> new Random ().nextInt(100 ) + 1 ); System.out.println(num1); System.out.println(num2); } public static int getRandomNumber (RandomNumberHandler randomNumberHandler) { return randomNumberHandler.getNum(); } } @FunctionalInterface interface RandomNumberHandler { int getNum () ; }
Lambda表达式简写
格式1:
1 MyFunction(() -> System.out.println("Lambda表达式简写形式一" ));
格式2:
1 MyFunction(msg -> System.out.println("Lambda表达式简写形式二" + msg));
格式3:
1 int n = MyFunction(() -> new Random ().nextInt(100 ) + 1 );
格式4:
1 int n = MyFunction((a , b) -> a + b);
Lambda和匿名内部类的区别
使用限制不同
匿名内部类:可以操作类、接口
Lambda表达式:只能操作函数式接口
实现原理不同
匿名内部类:编译之后,产生一个单独的.class字节码文件
Lambda表达式:编译之后,没有一个单独的.class字节码文件