Java面向对象 ( 下)
观看b站尚硅谷视频做的笔记
文章目录
- Java面向对象 ( 下)
- 1、 关键字:static
- 1.1、static 的使用
- 1.1.1、static 修饰属性
- 1.1.2、 static 修饰方法
- 1.1.3、 static 修饰代码块
- 1.1.4、 static 修饰内部类
- 1.1.5、类变量 vs 实例变量内存解析
- 1.2、 自定义 ArrayUtil 的优化
- 1.3、 static 的应用举例
- 1.4、单例 (Singleton) 设计模式
- 2、 理解 main 方法的语法(了解 )
- 3、类的成员之四:代码块
- 3.1 静态代码块
- 3.2 非静态代码块
- 4、关键字:final
- 4.1 final 修饰类
- 4.2 final 修饰方法
- 4.2 final 修饰变量
- 4.2.1final 修饰变量
- 4.2.2 final 修饰属性
- 4.2.2.1 显式化初试化
- 4.2.2.2 代码块初试化
- 4.2.2.3 构造器中初试化
- 4.2.3 final 修饰局部变量(方法内、形参)
- 4.2.3.1方法内
- 4.2.3.2 形参内
- 5、 抽象类与抽象方法
- 5.1 抽象类
- 5.2 抽象方法
- 5.3、 抽象类应用
- 5.4、创建抽象类的匿名子类对象
- 5.5、多态的应用:模板方法设计模式(TemplateMethod)
- 6、接口 (interface)
- 6.1 、接口的概述
- 6.2 、接口举例
- 6.3、 接口的应用:代理模式 (Proxy)
- 6.4、 接口的应用:工厂模式
- 7、Java 8 中关于接口的改进
- 8、类的成员之五:内部类
- 9、练习
- 9.1、static练习
- 9.2、抽象类练习
- 9.3、代码块练习
- 9.4、final练习
- 9.5、接口练习
- 10、面试题
- 10.1、
- 10.2、抽象类与接口有哪些异同?
- 10.4、
- 11、项目
1、 关键字:static
1.1、static 的使用
static关键字到底在哪里用?用来做什么?
其方法(非static)才可以供外部调用。
当我们编写一个类时,其实就是在描述其对象的属性和行为,而并没有产生实质上的对象,只有通过new关键字才会产生对象,这时系统才会分配内存空间给对象,其方法才可以供外部调用。
我们有时希望无论是否产生对象或无论产生了多少对象的情况下,某些特定的数据在内存空间里只有一份,例如所有的中国人都有个国家名称,每一个中国人都共享这个国家名称,不必在每一个中国人的实例对象中都单独分匹配一个用于代表国家名称的变量。
以上说了两件事儿,以前是new对象,new完对象才会给对象分配空间这时属性才会分配加载到空间,方法也可以通过对象调。
1.1.1、static 修饰属性
一、static关键字的使用
1)static 静态的
2)static 主要用来修饰类的结构(修饰属性、方法、代码块、内部类)
3)static 修饰类的属性
属性分静态属性(类变量),非静态属性(实例变量)
非静态属性(实例变量):创建类的多个对象,每个对象都独立拥有一套类中的非静态属性。当修改其中一个非静态属性时,不会导致其他对象中同样的属性值的修饰。
静态变量:创建类的多个对象,多个对象共享同一个静态变量。当通过静态变量去修改某一个变量时,会导致其他对象调用此静态变量时,是修改过后的。
4)static修饰属性的其他说明:
①静态变量随着类的加载而加载。可以通过类.静态变量的方式调用。
②静态变量的加载要早于对象的创建。
③由于类只会加载一次,则静态变量在内存中也只会存在一次,存在方法区的静态域中。
静态变量(类变量) 实例变量(非静态变量)
类 √ ❌
对象 √ √
5)静态属性举例:System.out,Math.PI
// 中国人
class Chinese{
String name;
int age;
static String nation;
}
此时的name age每个对象各自有一份
static String nation;静态的属性
通过某一个对象去修改某一个变量时,会导致其他对象调用此静态变量时,是修改过的。
( 实例变量 是归结于对象所有
类变量是归结于类所有)
类的加载早于对象,实例变量是在有了对象以后或在创建对象当中有了实列变量
在造对象前,nation就已经有了
直接可以类.属性
public class StaticTest {
public static void main(String[] args) {
Chinese.nation = " 中国 ";
③ 由于类只会加载一次,则静态变量在内存中也只会存在一次。存在方法区的静态域中。这就涉及Jvm内存加载,运行时,会把整个类加载到方法区,加载完后,会做一段时间的缓存,再去用的时候,还在,提升速度
加载一个Chinese()时,会把类本身缓存起来,回头再用是用缓存好的Chinese,在类的生命周期中,static属性就只有一份
静态域。(静态属性)
随着对象创建,实列变量就有了
注意
还没有对象。通过类去调实列变量不行
// 编译不通过
// Chinese.name = " 张继科 ";不能通过类去调实列变量
类名调属性
System.
out是system中的结构
想要用类中的非静态属性,先创建类的对象,通过对象.属性调用。
对于静态属性,不用造对象,直接调。
package com.zhou.java;
public class StaticTest {
public static void main(String[] args) {
Chinese.nation = "中国";//对于静态属性,不用造对象,直接类名调。
Chinese c1 = new Chinese();
c1.age = 40;
c1.name = "姚明";
c1.nation = "CHN";
Chinese c2 = new Chinese();
c2.age = 18;
c2.name = "谷爱凌";
c2.nation = "CHINA";
System.out.println(c1.age + c1.name);
System.out.println(c2.age + c2.name);
System.out.println(c1.nation);
Chinese c3 = new Chinese();
c3.nation = "china";
System.out.println(c1.nation);
//编译不通过
// 不能通过类名直接调用非静态属性:Chinese.name="姚明1";
}
}
class Chinese {
String name;//此时的name、age是每个对象都有一份
int age;
static String nation;
}
1.1.2、 static 修饰方法
一、使用 static 修饰方法:静态方法。
1)随着类的加载而加载,可以通过类.静态方法的方式调用
2) 静态方法 非静态方法
类 √ ×
对象 √ √
3)静态方法中,只能调用静态的方法或属性。
非静态的方法中,可以调用所有的方法或属性。
4)static注意点:
在静态方法内,不能使用this、super关键字。
关于静态属性和静态方法的使用,从生命周期角度理解。
5)开发中,如何确定一个属性是否需要声明static?
属性是可以被多个对象共享,不会随着对象不同而不同。
类中的常量也常常声明为static
开发中,如何确定一个方法是否需要声明static?
操作静态属性的方法,通常设置为static
工具类中的方法,习惯声明为static
比如Math、Arrays、Collections
静态方法与非静态方法的区别
非静态方法是通过对象调用
public void eat(){
System.out.println(" 中国人吃中餐 ");
// 调用非静态结构
this.info();
System.out.println("name : " + name);
// 调用静态结构
walk();
System.out.println(“nation : " + Chinese.nation);
}
现在的静态方法,随着类的记载而加载
public static void show(){
System.out.println(” 我是一个中国人! ");
有类的时候就可以去调用
方法里是可以调方法或者属性,比较关心的是在静态方法中是否能调非静态的方法、静态方法?能否调静态属性、非静态属性?
静态方法中,只能调用静态的方法或属性。(因为生命周期一致)
非静态的方法中,可以调用静态方法或属性,也可以调非静态或属性。
public static void show(){
System.out.println(" 我是一个中国人! ");
// eat(); 此处省略了this,即就是在静态方法中不可调用this,this表示当前对象,现在静态中没有对象,即不可以用this
System.out.println("nation : " + Chinese.nation);对静态属性来说前面省略的不是this,而是省略类.
静态方法
非静态结构是与对象同步,静态结构是与类的加载同步,所以晚出生的可以调早出生的,早出生的不可以调晚出生的
生命周期的角度去理解。(对象的生命周期或者类的生命周期)
package com.zhou.java;
public class StaticTest01 {
public static void main(String[] args) {
Chinese.nation = " 中国 ";
Chinese1 c1 = new Chinese1();
c1.eat();
Chinese1.show();
}
}
class Chinese1{
String name;
int age;
static String nation;
public void eat(){
System.out.println("中国人吃午餐");
}
public static void show(){
System.out.println("我是一个中国人");
//eat();
//name="tom";
nation="china1";
}
}
开发中,如何确定一个属性是否需要声明 static 的?
1.1.3、 static 修饰代码块
1.1.4、 static 修饰内部类
1.1.5、类变量 vs 实例变量内存解析
栈主要存局部变量
方法区:类的加载信息(反射)、静态域、常量池(Stirng)
上图中的代码是放到main方法中,涉及到的变量都为局部变量
Chinese.nation = " 中国 ";此处用了Chinese,就涉及到类的加载
静态的属性会随着类的加载而加载
意味着第一行代码在堆空间中,这就属于静态域,针对于Chinese这个类,这个nation加载有一个初始化值
第一行代码
第二行代码
栈空间声明个c1,堆空间中new了一个对象,实列变量(name,age)存在于堆空间中,先默认初始化值,首地址赋给c1,c1通过地址值指向堆空间的chinese对象。
第三、四行代码,通过对象调属性,将name,age重新赋值
姚明其实没在图上的位置,其实是在常量池中,是通过地址指过来的
name,age是每个对象都有一份
Chinese c2 = new Chinese();又声明了一个c2
c1调nation,nation是加载到类里面,静态结构
通过对象(对象是Chinese的)即可以调nation,将nation改成CHN,再通过c2去调nation,即还是CHA,c2再改nation改成CHINA,后面不管通过什么去调,nation的值都是CHINA
通过以上了解静态属性跟原来的图不一样了且只有一份
1.2、 自定义 ArrayUtil 的优化
讲完方法的时候,做过一个类
1.3、 static 的应用举例
造了多少个对象,用一个变量来记录一下
id是自动赋值,那每次造对象时,id属性都赋好值了
没有显式赋值就在构造器中赋值
此时的id为1001
第一次为1001,第二次为1002
现在是造了两个对象,调total时告诉为2
根具体对象没有关系,通过类可以直接调用,每操作一次,加一下
属性为static,操作属性的方法一般也是static
若希望radius赋值,就提供一个构造器
package com.zhou.java;
public class CircleTest {
public static void main(String[] args) {
Circle c1= new Circle();
Circle c2= new Circle();
System.out.println(c1.getId());
System.out.println(c2.getId());
System.out.println(Circle.getTotal()
);
}
}
class Circle{
private int id;
private double radius;
private static int total;
private static int init=1001;
public Circle(){
id=init++;
total++;
}
public double findArea(){
return 3.14 *radius*radius;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public double getRadius() {
return radius;
}
public void setRadius(double radius) {
this.radius = radius;
}
public static int getTotal() {
return total;
}
public static void setTotal(int total) {
Circle.total = total;
}
public Circle(double radius){
this();
this.radius=radius;
// id=init++;
// total++;
}
}
1.4、单例 (Singleton) 设计模式
一、设计模式是**在大量的实践中总结和理论化之后优选的代码结构,编程风格,以及解决问题的思考方式。 设计模式免去我们自己在思考和探索。就像是经典的棋谱,不同的棋局,我们用不同的棋谱。
二、所谓类的单例设计模式。就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取的其对象实例的方法如果我们要让类在一个虚拟机中只能产生一个对象,我们首先必须将类的构造器的访问权限设置为private,这样就不能用new操作符在外部产生类的对象了,但在类内部仍可以产生该类的对象。因为在类的外部开始无法得到类的对象,只能调用该类的静态某个静态方法已返回类内部创建的对象,静态方法只能访问类中的静态变量,所以指向类内部产生该类对象的变量也必须定义成静态的。
三、单例设计模式:
1)所谓的类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例。
2)如何让实现?
饿汉式,懒汉式
3)区分饿汉式和懒汉式
饿汉式好处:线程安全
坏处:对象加载时间过长
懒汉式好处:延迟对象的创建
坏处:线程不安全
4)单例模式的饿汉式
package com.zhou.java;
public class SingTest {
}
class Bank{
private Bank(){
}
private static Bank instance= new Bank();
public static Bank getInstance(){
return instance;
}
}
5)单例模式的懒汉式
package com.zhou;
public class SingletonTest {
}
class Order{
private Order(){
}
private static Order instance=null;
public static Order getInstance(){
if(instance==null){
instance=new Order();
}
return instance;
}
}
6)单例模式的优点
由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要较多的资源时,如读取配置,产生其他依赖,则可以通过在应用启动时直接产生一个单列对象,然后永久驻留内存的方式来解决。
7)单例设计模式–应用场景
网站的计数器::一般都使用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。
数据库连接池的设计一般也是采用单例模式,因为数据库是一种数据库资源。
项目中,读取配置文件的类,一般也只有一个对象,没有必要每次使用配置文件,都生成一个对象去读取。
static的应用
单例 (单独的实例),实例(对象),只想造一个类对象。
创建一个类是单例,只造一个对象
//1. 私有化类的构造器,防止在类的外面造多个对象
//2. 内部创见类的对象
private static Bank instance = new Bank();也可理解为他的一个属性。
不让造对象的话,需要用这个对象,方法就是
//3. 提供公共的静态的方法,返回类的对象。
Bank bank1 = Bank.getInstance();
Bank bank2 = Bank.getInstance();这俩是同一个对象吗?
System.out.println(bank1 == bank2);
单例模式的懒汉式
//2. 声明当前类对象,没有初始化。
// 此对象也必须声明为 static 的
private static Order instance = null;
造对象这件事儿放在方法中提供
此处是每调一次方法,就new一个对象,会创建多个类的对象
package com.zhou.java;
public class SingletonTest2 {
public static void main(String[] args) {
Order instance1 = Order.getInstance();
Order instance12= Order.getInstance();
System.out.println(instance1==instance12);
}
}
class Order{
private Order(){
}
private static Order instance=null;
public static Order getInstance(){
if (instance==null){
instance= new Order();
}
return instance;
}
}
啥时候用啥时候造,这是懒汉式
上来就造个对象,这是饿汉式
区分饿汉式和懒汉式。
懒汉式为啥不安全?
假设两个线程,这两个线程都想去获取当前类的对象,对象只有一个,大家获取的是同一个地址,首次调,instance进去if结构里,没有马上new,稍微停了一下(线程阻塞),阻塞期间,另一个线程也进来,是null也进入if结构里,又new了一个对象,就有两个对象,即线程不安全
2、 理解 main 方法的语法(了解 )
1)由于Java虚拟机需要调用类的main方法,所以该方法的访问权限必须是
main () 方法也是一个普通的静态方法;也可以通过类去调。
在源文件中只能声明一个public的类,没说每个类中去写各自的main方法
Main.main(new String[100]);需要传具体数组
main () 方法也可以作为我们与控制台交互的方式。
( 之前,使用 Scanner)
假设拿到值,做简单输出,由于是数组,就写for遍历
怎么交互把数据存到数组中?
3、类的成员之四:代码块
第一条主线:类的成员:方法 属性 构造器 代码块 内部类
第二条主线:封装 继承 多态
第三条主线:关键字的使用(static,final,abstatct 、interface)
1)代码块的作用:用来初试化类,对象
2)代码块如果有修饰的话:只能使用static
3)分类:静态代码块,非静态代码块
3.1 静态代码块
4)静态代码块:
内部可以有输出语句
随着类的加载而执行,而且只执行一次
作用:初始类的信息
如果一个类中,定义了多个静态代码块,则按照声明的先后顺序执行
静态代码块的执行,优先于非静态代码块的执行。
静态代码块只能调用静态的属性、静态的方法,不能调用非静态的结构。
3.2 非静态代码块
内部可以有输出语句
随着对象的创建而执行,每创建一个对象,就执行一次非静态代码块。
作用:可以在创建对象时,对象的属性等进行初试化。
如果一个类中,定义了多个非静态代码块,则按照声明的先后顺序执行。
非静态代码块可以调用静态的属性、静态的方法、非静态的属性、非静态的方法。
对象属性可以赋值的位置:
默认初试化
显式初试化
构造器中初试化
有了对象以后,可以通过对象.属性或对象.方法的方式,进行赋值。
在代码块中赋值。
构造器里的结构也是初试化后的值
stattic中输出语句怎么执行?
先不造对象,类调属性,就会加载到内存中,通常静态的结构都会加载,静态属性静态方法随着类的加载而加载
package com.zhou;
public class BlockTest {
public static void main(String[] args) {
String desc = Person.desc;
}
}
class Person{
String name;
int age;
static String desc="我是一个人";
public Person(){
}
public Person(String name,int age){
this.age=age;
this.name=name;
}
static {
System.out.println("static");
}
{
System.out.println("非静态语句");
}
public void eat(){
System.out.println("吃饭");
}
@Override
public String toString() {
return "Person [name="+name+",age"+age+"]";
}
public static void info(){
System.out.println("快乐的人");
}
}
静态方法虽随着类的加载而加载,但是并没有执行,要通过类.才可以调用
静态代码块不仅随着类的加载而加载,而且还执行了
非静态代码块是随着对象
先创建对象
随着类的加载而执行,而且只执行一次(只要当前类没有加载,就不会执行)
package com.zhou;
public class BlockTest {
public static void main(String[] args) {
String desc = Person.desc;
Person person1 = new Person();
**Person person2 = new Person();
Person.info();
}
}
class Person{
String name;
int age;
static String desc="我是一个人";
public Person(){
}
public Person(String name,int age){
this.age=age;
this.name=name;
}
static {
System.out.println("static");
}
{
System.out.println("非静态语句");
}
public void eat(){
System.out.println("吃饭");
}
@Override
public String toString() {
return "Person [name="+name+",age"+age+"]";
}
public static void info(){
System.out.println("快乐的人");
}
}**
非静态代码块可以在创建对象时,对对象的属性等进行初始化
package com.zhou;
public class BlockTest {
public static void main(String[] args) {
String desc = Person.desc;
Person person1 = new Person();
Person person2 = new Person();
System.out.println(person1.age);
Person.info();
}
}
class Person{
String name;
int age;
static String desc="我是一个人";
public Person(){
}
public Person(String name,int age){
this.age=age;
this.name=name;
}
static {
System.out.println("static");
}
{
System.out.println("非静态语句");
age=1;
}
public void eat(){
System.out.println("吃饭");
}
@Override
public String toString() {
return "Person [name="+name+",age"+age+"]";
}
public static void info(){
System.out.println("快乐的人");
}
}
属性赋值又多了一个位置:
作用:初始化类的信息(初始类的静态属性)
在静态代码块中调用静态属性
静态代码块可以定义多个,执行先后顺序
静态代码块和非静态代码块谁先谁后?
静态代码块
一般情况下,没有必要造多个静态代码块,非静态代码块
静态中只能调用静态,随着类加载而加载,此时还没有圈出来的结构
非静态中能调用静态和非静态
4、关键字:final
fina:最终的
final 可以用来修饰的结构:类、方法、变量。
static final 用来修饰:全局常量。
4.1 final 修饰类
final 用来修饰一个类:此类不能被其他类所继承。比如:String 类、System 类、StringBuffer 类。
final 用来修饰一个类:此类不能被其他类所继承。
比如:String 类、System 类、StringBuffer 类。
String类为啥加final?
String表示的是字符串,实际里面封装的是Char型数组。相当于对char数组进行操作,不让继承,相当于不让你在String上扩充功能。字符串相关的功能该写的都写完整了
4.2 final 修饰方法
final 修饰一个方法:final 标记的方法不能被子类重写。比如:Object 类中的 getClass()。
final 标记的方法不能被子类重写。
比如:Object 类中的 getClass()。
Object 类中的 getClass(),获取当前对象所属的类
native相当于是调用底层c语言的代码
4.2 final 修饰变量
4.2.1final 修饰变量
final 用来修饰变量:此时的 " 变量 "( 成员变量或局部变量 ) 就是一个常量。名称大写,且只能被赋值一次。
4.2.2 final 修饰属性
final 修饰属性,可以考虑赋值位置有:显式初始化、代码块中初始化、构造器中初始化;
final 修饰属性,是常量,一旦赋值以后,就不能再赋值,再哪些位置给所谓的属性进行赋值。
尤其是使用 final 修饰形参时,表明此形参是一个常量。当我们调用此方法时,给常量形参赋一个实参。一旦赋值以后,就只能在方法体内使用此形参,但不能进行重新赋值。
没有说用来修饰属性(属性只是属于变量中的一种)
num = num + 5;5也称为常量
4.2.2.1 显式化初试化
4.2.2.2 代码块初试化
4.2.2.3 构造器中初试化
在构造器中赋值,就需要考虑每个构造器需要赋值上,要不然通过某个构造器造对象时,有的有能赋值上,有的没有赋值不了。
构造器,刚写完构造器为啥就报错?
public FinalTest(int n){
}
上面构造器中有RIGHT=2;这个构造器也需要有一个RIGHT=2
通过方法给final属性赋值不靠谱,为什么?
构造器是对象出生的最后一个关卡,构造器调完就出生,对象一出生,内存中相应的属性就加载了,就得有值,通过对象的属性去调属性,只是说会再修改,常量不能再修改
这三个位置再谈有没有一些小细节的点
造多个对象,属性值都一样。就没必要在构造器中赋值了
每个对象的属性值不一样,就在构造器中赋值
如果此时属性不是简单的值,而是调了一个方法,且方法有异常还得处理,就可以在代码块中
4.2.3 final 修饰局部变量(方法内、形参)
4.2.3.1方法内
show方法定义了一个num的数值,该数值没有定义成final可以进行赋值操作
将数值定义成final就不可以进行赋值操作,此时的NUM是一个常量
4.2.3.2 形参内
尤其是使用 final 修饰形参时,表明此形参是一个常量。
当我们调用此方法时,给常量形参赋一个实参。一旦赋值以后,就只能在方法体内使用此形参,但不能进行重新赋值。
static final 用来修饰属性:全局常量。
此时这么写没有赋值也没有报错(因为,形参赋值是调用时才赋值)
用final修饰的num不能再操作
5、 抽象类与抽象方法
第一条主线:类的成员:方法 属性 构造器 代码块 内部类
第二条主线:封装 继承 多态
第三条主线:关键字的使用(static,final,abstatct 、interface)
父类不再造对象,怎么证明不在造对象,就用abstract,就不可以再去new对象
前面说:属性是一个变量,只要能调就可以用,不想变就加final
5.1 抽象类
abstract测试:
package com.zhou.java;
public class AbstractTest {
public static void main(String[] args) {
Person p1 = new Person();
p1.eat();
}
}
class Person{
String name;
int age;
public void eat(){
System.out.println("人吃饭");
}
public void walk(){
System.out.println("人走路");
}
public Person(){
}
public Person( String name,int age){
this.name= name;
this.age= age;
}
}
加上abstract之后
①此类不能实例化;
② 抽象类中一定有构造器,便于子类实例化时调用 ( 涉及:子类对象实例化全过程 );
③开发中,都会提供抽象类的子类,让子类对象实例化,实现相关的操作
加上abstarct后不能再造对象,造对象这个功能是构造器来做,问还有构造器吗?
有构造器,此时构造器功能:自己不能造对象不能调构造器,创建子类对象时一定会调父类构造器,功能不只一个,一个是自己造对象,另一个是子类也要调构造器,子类对象实例化的全过程
在student子类中写一个构造器
造student对象时,还是会用到构造器
package com.zhou.java;
public class AbstractTest {
public static void main(String[] args) {
/*一旦Person类抽象了,就不可实例化
Person p1 = new Person();
p1.eat();*/
}
}
abstract class Person{
String name;
int age;
public void eat(){
System.out.println("人吃饭");
}
public void walk(){
System.out.println("人走路");
}
public Person(){
}
public Person( String name,int age){
this.name= name;
this.age= age;
}
}
class Student extends Person{
public Student(String name,int age){
super(name,age);
}
}
5.2 抽象方法
abstract 修饰方法:抽象方法;
抽象方法,只有方法的声明,没有方法体;
包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象方法;
若子类重写了父类中所有的抽象方法(不管是Student类的父类Person还是Person后还有一个父类,此子类方可实例化;
若子类没有重写父类中的所有抽象方法,则此子类也是一个抽象类,需要使用 abstract 修饰;
抽象方法没有方法体还能不能执行(不能被调用)
方法要想被调,需要用对象调,方法写在Person里,就需要造Person对象。能造Person对象,就能调class Person前就不需要加abstrct,但是会报错
如果在一个类中出现抽象方法,就得保证所在类不能造对象
(假如能造对象,就能调抽象方法,但是抽象方法又没有方法体)
怎么保证Person不能造对象?类即为抽象类
包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有
抽象方法(只是不想让自己这个类造对象)
此时报错,为啥报错?
抽象方法,没有抽象方法,被子类继承,Student类就相当于有两个方法,对于walk()方法,造个对象就可以调用,student对象不能调eat()抽象方法,怎么做可以让他不报错?
①重写eat()方法
package com.zhou.java;
public class AbstractTest {
public static void main(String[] args) {
/*一旦Person类抽象了,就不可实例化
Person p1 = new Person();
p1.eat();*/
}
}
abstract class Person{
String name;
int age;
//不是抽象方法
/* public void eat(){
System.out.println("人吃饭");
}*/
public abstract void eat();
public void walk(){
System.out.println("人走路");
}
public Person(){
}
public Person( String name,int age){
this.name= name;
this.age= age;
}
}
class Student extends Person{
public Student(String name,int age){
super(name,age);
}
@Override
public void eat() {
System.out.println("学生吃有营养的饭");
}
}
②类为抽象类
没有重写第一种方式,类为抽象类
package com.zhou.java;
public class AbstractTest {
public static void main(String[] args) {
/*一旦Person类抽象了,就不可实例化
Person p1 = new Person();
p1.eat();*/
}
}
abstract class Person{
String name;
int age;
//不是抽象方法
/* public void eat(){
System.out.println("人吃饭");
}*/
public abstract void eat();
public void walk(){
System.out.println("人走路");
}
public Person(){
}
public Person( String name,int age){
this.name= name;
this.age= age;
}
}
abstract class Student extends Person{
public Student(String name,int age){
super(name,age);
}
}
若子类重写了父类中所有的抽象方法(不管是Student类的父类Person,还是Person类还有一个父类Creature类,问Person类中有几个抽象方法(两个,一个自己的还有一个父类的抽象方法)
package com.zhou.java;
public class AbstractTest {
public static void main(String[] args) {
/*一旦Person类抽象了,就不可实例化
Person p1 = new Person();
p1.eat();*/
}
}
abstract class Creature{
public abstract void breath();
}
abstract class Person extends Creature{
String name;
int age;
//不是抽象方法
/* public void eat(){
System.out.println("人吃饭");
}*/
public abstract void eat();
public void walk(){
System.out.println("人走路");
}
public Person(){
}
public Person( String name,int age){
this.name= name;
this.age= age;
}
}
abstract class Student extends Person{
public Student(String name,int age){
super(name,age);
}
}
package com.zhou.java;
public class AbstractTest {
public static void main(String[] args) {
/*一旦Person类抽象了,就不可实例化
Person p1 = new Person();
p1.eat();*/
}
}
abstract class Creature{
public abstract void breath();
}
abstract class Person extends Creature{
String name;
int age;
//不是抽象方法
/* public void eat(){
System.out.println("人吃饭");
}*/
public abstract void eat();
public void walk(){
System.out.println("人走路");
}
public Person(){
}
public Person( String name,int age){
this.name= name;
this.age= age;
}
}
class Student extends Person{
public Student(String name,int age){
super(name,age);
}
@Override
public void breath() {
System.out.println("学生应该呼吸新鲜空气 ");
}
@Override
public void eat() {
System.out.println("学生应该吃有营养的饭");
}
}
5.3、 抽象类应用
几何图形
最初写几何图形纠结,求面积时图形不知道是什么?方法就没法写,写方法很困难,现在可以写成抽象方法
abstract 使用上的注意点:
1.abstract 不能用来修饰变量、代码块、构造器;
2.abstract 不能用来修饰私有方法、静态方法、final 的方法、final
的类。
5.4、创建抽象类的匿名子类对象
匿名:一般都只用一次
之前讲类和对象时,讲过匿名对象。
类也有匿名类。
对象没有名,类名是一个Student类,不一定非得用匿名对象,也可以进行显式的赋给变量名
匿名子类也是一样
不想用匿名的类,就显示抽象类去提供一个子类
调method1时,需要传Person的对象,person本身是抽象,只能去new一个子类的对象,赋给一个具体的变量
抽象类的匿名子类
Person p=子类的对象(此时的子类不知道类名),理解为抽象类的子类Person
为啥调的是吃东西,好好呼吸
多态的使用
父类的变量
实际子类的对象
编译是调父类的方法
实际运行的是子类对象的方法
上面是匿名类,有名对象
还可造匿名子类匿名对象
5.5、多态的应用:模板方法设计模式(TemplateMethod)
测试哪段代码不确定
6、接口 (interface)
6.1 、接口的概述
接口的使用:
1)接口使用interface来定义
2)在Java中:接口和类是并列的两个结构。
3)如何去定义两个接口:定义接口中的成员。
jdk7及之前的版本:只能定义全局常量、抽象方法。
全局常量:public static final的,但是书写中,可以省略不写
抽象方法:public abstract
jdk8:除了全局常量和抽象方法之外,还可以定义静态方法、默认方法。
4)接口中不能定义构造器!意味着接口不可以实列化
5)java开发中,接口通过让类去实现的方式来使用。
如果实现类覆盖了接口中所有的抽象方法,则此实现类就可以实列化。
如果实现类没有覆盖接口中所有的抽象方法,则此实现类仍为一个抽象类。
类是单继承,一个类可以实现多个接口
接口中的属性都是public
全局常量:public static final 的,但是书写中,可以省略不写。
飞功能封装在接口中
接口中的静态结构也可以通过接口直接调
体现的是静态的事
System.out.println(Flayable.MAX_SPEED);
System.out.println(Flayable.MIN_SPEED);
接口中不能定义构造器!意味着接口不可以实例化
public Flayable(){
}
前面讲抽象类也不可以实列化,但是抽象类是有构造器的,抽象类的构造器是为了实列化子类
java开发中,接口通过让类去实现(implements)的方式来使用。
①如果实现类覆盖了接口中所有的抽象方法,则此实现类就可以实例化。
②如果实现类没有覆盖接口中所有的抽象方法,则此实现类仍为一个抽象类。
抽象类是讲实现,继承类是讲重写
Java类可以实现多个接口 —>弥补了 Java 单继承性的局限性,
interface Attackable{
void attack();//省略了抽象方法
}
class Bullet extends Object implements Flayable,Attackable,CC{
格式:class AAextends BBimplements CC,DD,EE。
public class InterfaceTest {
public static void main(String[] args) {
System.out.println(Flayable.MAX_SPEED);
System.out.println(Flayable.MIN_SPEED);
}
}
interface Flayable{
public static final int MAX_SPEED=7900;
int MIN_SPEED=1;//省略了public static final
/* public Flayable(){
}
*/
public abstract void fly();
void stop();//省略了public abstract
}
interface Attackable{
void attack();
}
class Plane implements Flayable{
@Override
public void fly() {
System.out.println("飞机通过引擎起飞");
}
@Override
public void stop() {
System.out.println("驾驶员减速停止");
}
}
class Bullet extends Object implements Flayable,Attackable,CC{
@Override
public void fly() {
}
@Override
public void stop() {
}
@Override
public void attack() {
}
@Override
public void method1() {
}
@Override
public void method2() {
}
}
interface AA{
void method1();
}
interface BB{
void method2();
}
interface CC extends AA,BB{
}
接口和类之间的关系称为实现,类和类之间的关系称为继承,接口与接口之间也叫继承,而且可以多继承。
6.2 、接口举例
(一)接口的具体使用,体现多态性。
接口的主要用途就是被实现类实现。(面向接口编程)
抽象类和接口有一个共性,都不可以实列化
如果方法的形参如果写成一个抽象类或接口,用的话就必须体现多态的特性,提供子类的对象
电脑传输数据,来个电脑,造个电脑对象
电脑直接调transferData,transferData里需要传usb对象,接口不可以造对象,只能造他的实现类对象,体现了他的多态性
public void transferData(USB usb){ //声明的是usb,实际new的是实现类对象
编译调的是抽象方法,实际执行的是实现类重写后的方法
jdbc其实就是操作数据库的一些规范,绝大部分都是一些接口
public class USBTest {
public static void main(String[] args) {
Computer com = new Computer();
Flash flash = new Flash();
com.transferData(flash);
}
}
class Computer{
public void transferData(USB usb){
usb.start();
System.out.println("具体传输数据的细节");
usb.stop();
}
}
interface USB{
//常量:定义了长、宽、最大、最小的传输速度等
void start();
void stop();
}
class Flash implements USB{
@Override
public void start() {
System.out.println("u盘开始工作");
}
@Override
public void stop() {
System.out.println("u盘结束工作");
}
}
(二)匿名实现类
创建了接口的非匿名实现类的匿名对象com.transferData(new Printer());
创建了接口的匿名实现类的非匿名对象(手机也可以跟电脑连,USB接口的匿名实现类,但对象有名叫phone
USB phone = new USB(){//创建了匿名实现类,没有名,就拿USB充当一下
创建了接口的匿名实现类的匿名对象
public class USBTest {
public static void main(String[] args) {
Computer com = new Computer();
//1.创建了接口的非匿名实现类的非匿名对象
Flash flash = new Flash();
com.transferData(flash);
//2.创建接口的非匿名实现类的匿名对象
com.transferData(new Printer());
//3.创建接口的匿名实现类的的非匿名对象
USB phone= new USB(){
@Override
public void start() {
System.out.println("手机开始工作");
}
@Override
public void stop() {
System.out.println("手机结束工作");
}
};
com.transferData(phone);
//4.创建接口的匿名实现类的匿名对象
com.transferData(new USB() {
@Override
public void start() {
System.out.println("MP3开始工作");
}
@Override
public void stop() {
System.out.println("MP3结束工作");
}
});
}
}
class Computer{
public void transferData(USB usb){
usb.start();
System.out.println("具体传输数据的细节");
usb.stop();
}
}
interface USB{
//常量:定义了长、宽、最大、最小的传输速度等
void start();
void stop();
}
class Flash implements USB{
@Override
public void start() {
System.out.println("u盘开始工作");
}
@Override
public void stop() {
System.out.println("u盘结束工作");
}
}
class Printer implements USB{
@Override
public void start() {
System.out.println("打印机开始工作");
}
@Override
public void stop() {
System.out.println("打印机结束工作");
}
}
6.3、 接口的应用:代理模式 (Proxy)
6.4、 接口的应用:工厂模式
7、Java 8 中关于接口的改进
java8中,接口可以添加静态方法和默认方法。
从技术角度来说,这是完全合法的,只是他看起来违反了接口作为一个抽象定义的理念
静态方法:
使用static关键字修饰。可以通过接口直接调用静态方法,并执行方法体。我们经常在相互一起使用的类中使用静态方法。可以在标准库中找到像collection、或者collections这样成对的接口和类。
默认方法:
默认方法使用 default 关键字修饰。可以通过实现类对象来调用。我们在已有的接口中提供新方法的同时,还保持了与旧版本代码的兼容性。比如:java 8 API 中对 Collection、List、Comparator 等接口提供了丰富的默认方法。
静态方法说白了就可以通过接口直接去调
默认方法需要弄个实现类,实现类的对象里默认方法是一个普通的非静态的方法一样
接口中定义的静态方法,实现类根本拿不到
package com.zhou.java;
public class SubClassTest {
public static void main(String[] args) {
SubClass s= new SubClass();
// s.method1();
//知识点1:接口中定义的静态方法只能通过接口自己调用
CompareA.method1();
//知识点2:通过实现类的对象,可以调用接口中的默认方法
//如果实现类重写了接口中的默认方法,调用的是重写的方法
s.method2();
//知识点3:如果子类或实现类继承的父类和实现类的接口中声明了同名参数的方法,
// 那么子类在没有重写此方法的情况下,默认调用的是父类中同名参数的方法。
s.method3();
//知识点4:如果实现类实现了多个接口,而这多个接口中定义了同名参数的默认方法,
// 那么在实现类中没有重写方法的情况下,报错。接口冲突,我们必须要在实现类中重写此方法。
}
}
class SubClass implements CompareA,CompareB{
public void method2(){
System.out.println("SubClass,上海");
}
public void method3(){
System.out.println("SubClass,深圳");
}
}
知识点5:如何让子类或实现类的方法中调用父类接口中被重写的方法。
前面讲子类、父类时,父类中有一个方法,子类去重写,重写完后再去调是子类的方法,如果在子类中有需要调父类的方法就用super关键字
在实现类中定义一个普通方法
想调接口中的默认方法
用接口.super.方法;
package com.zhou.java;
public class SubClassTest {
public static void main(String[] args) {
SubClass s= new SubClass();
// s.method1();
//知识点1:接口中定义的静态方法只能通过接口自己调用
CompareA.method1();
//知识点2:通过实现类的对象,可以调用接口中的默认方法
//如果实现类重写了接口中的默认方法,调用的是重写的方法
s.method2();
//知识点3:如果子类或实现类继承的父类和实现类的接口中声明了同名参数的方法,
// 那么子类在没有重写此方法的情况下,默认调用的是父类中同名参数的方法。
s.method3();
//知识点4:如果实现类实现了多个接口,而这多个接口中定义了同名参数的默认方法,
// 那么在实现类中没有重写方法的情况下,报错。接口冲突,我们必须要在实现类中重写此方法。
}
}
class SubClass extends SuperClass implements CompareA,CompareB{
public void method2(){
System.out.println("SubClass,上海");
}
public void method3(){
System.out.println("SubClass,深圳");
}
public void myMethod(){
method3();//调用自己定义的重写的方法
super.method3();//调用的是父类中声明的
CompareA.super.method3();
CompareB.super.method3();
}
}
8、类的成员之五:内部类
一)第一条主线:
类的成员:方法 、属性 、构造器 、代码块、内部类
二)第二条主线:
封装 继承 多态
三)第三条主线:
关键字的使用(static,final,abstatct 、interface)
①Java中允许将一个类A声明在另一个类B中,则类A 就是内部类,类B就是外部类。
②内部类的分类:成员内部类(静态成员内部类、非静态成员内部类)
局部内部类(方法内、代码块内、构造器内)。
③成员内部类(类里面,方法外面):
作为一个类:
类里面可以定义属性、方法、构造器等
类可以被final修饰,表示此类不能被继承,言外之意,不使用final,可以被继承。
类可以被abstract修饰
作为外部类的成员:
调用外部类的结构。
可以被static修饰
可以被4种不同权限修饰
④关注如下的3个问题:
如何实例化成员内部类的对象
如何在成员内部类中区分调用外部结构
开发中局部内部类的使用
成员内部类(直接定义在方法外、类里面),局部内部类(方法内、构造器内、代码块内)
可以被abstract修饰,表明不可以被实列化
package com.zhou.java;
public class InnClassTest {
}
class Person{
//静态成员内部类:方法外,类里面
abstract static class Dog{
String name;
public void show(){
System.out.println("卡拉是狗");
}
}
//非静态成员内部类:方法外,类里面
final class Bird{
String name;
public void Sing(){
System.out.println("我是一只小小鸟");
}
public Bird(){}
}
public void method(){
//局部内部类
class A{
}
}
//局部内部类
public Person(){
class B{
}
}
//局部内部类
{
class C{
}
}
//局部内部类
static {
class D{
}
}
}
eat前省略了Person.this
static 主要用来修饰类的内部成员,非构造器的四个,外部内肯定不能用static来修饰
讲类的时候有权限修饰符,
package com.zhou.java;
public class InnClassTest {
}
class Person{
String name;
int age;
public void eat(){
System.out.println("人:吃饭");
}
//静态成员内部类:方法外,类里面
static class Dog{
String name;
public void show(){
System.out.println("卡拉是狗");
// eat();静态成员内部类不可以调用非静态的方法
}
}
//非静态成员内部类:方法外,类里面
class Bird{
String name;
public void Sing(){
System.out.println("我是一只小小鸟");
//eat前省略了Person.this
Person.this. eat();//非静态成员内部类可以调用外部类的非静态方法
}
public Bird(){}
}
public void method(){
//局部内部类
class A{
}
}
//局部内部类
public Person(){
class B{
}
}
//局部内部类
{
class C{
}
}
//局部内部类
static {
class D{
}
}
}
有对象后去调成员内部类的方法,调方法涉及到调自己定义的属性、外部类的属性
person是一个外部类,现在想创建一个person、bird的实列
非静态要有外部类的实列才能有非静态的结构
类的内部结构,若是非静态的,有了对象都是p.
package com.zhou.java;
public class InnClassTest {
public static void main(String[] args) {
//创建Dog实例(静态成员内部类)
Person.Dog dog= new Person.Dog();
dog.show();
//创建bird实例(非静态的成员内部类)
// Person.Bird bird= new Person.Bird();错误的
Person p= new Person();
Person.Bird bird = p.new Bird();
bird.sing();
}
}
class Person{
String name;
int age;
public void eat(){
System.out.println("人:吃饭");
}
//静态成员内部类:方法外,类里面
static class Dog{
String name;
public void show(){
System.out.println("卡拉是狗");
// eat();静态成员内部类不可以调用非静态的方法
}
}
//非静态成员内部类:方法外,类里面
class Bird{
String name;
public void sing(){
System.out.println("我是一只小小鸟");
//eat前省略了Person.this
Person.this. eat();//非静态成员内部类可以调用外部类的非静态方法
}
public Bird(){}
}
public void method(){
//局部内部类
class A{
}
}
//局部内部类
public Person(){
class B{
}
}
//局部内部类
{
class C{
}
}
//局部内部类
static {
class D{
}
}
}
如何在成员内部类中区分调用外部类的结构
同名(以属性为例)
内部类属性
有三个name,怎么在方法中区分name
先调形参
package com.zhou.java;
public class InnClassTest {
public static void main(String[] args) {
//创建Dog实例(静态成员内部类)
Person.Dog dog= new Person.Dog();
dog.show();
//创建bird实例(非静态的成员内部类)
// Person.Bird bird= new Person.Bird();错误的
Person p= new Person();
Person.Bird bird = p.new Bird();
bird.sing();
bird.display("黄鹂");
}
}
class Person{
String name="小明";
int age;
public void eat(){
System.out.println("人:吃饭");
}
//静态成员内部类:方法外,类里面
static class Dog{
String name;
public void show(){
System.out.println("卡拉是狗");
// eat();静态成员内部类不可以调用外部类的非静态的方法
}
}
//非静态成员内部类:方法外,类里面
class Bird{
String name="杜鹃";
public void sing(){
System.out.println("我是一只小小鸟");
//eat前省略了Person.this
Person.this. eat();//非静态成员内部类可以调用外部类的非静态方法
}
public void display(String name){
System.out.println(name);//方法的形参
System.out.println(this.name);//内部类的属性
System.out.println(Person.this.name);//外部类的属性
}
}
public void method(){
//局部内部类
class A{
}
}
//局部内部类
public Person(){
class B{
}
}
//局部内部类
{
class C{
}
}
//局部内部类
static {
class D{
}
}
}
开发中局部内部类的使用
局部内部类(方法中、代码块中、构造器中直接声明内部类)
调方法时返回一个Compare接口(里面提供一个实现类,同时造对象且返回)
方式二实现类没有名,接口也没有名
方式1是有名的实现类的匿名对象
package com.zhou.java;
public class InnerTest1 {
//开发中很少见
public void method() {
//局部内部类
class AA { }
}
//创建一个实现了Comparable接口的类的对象
//方式1:
public Comparable getComparable() {
class MyCompable implements Comparable {
@Override
public int compareTo(Object o){
return 0;
}
}
// return new MyCompable();
//方式二:
return new Comparable(){
@Override
public int compareTo(Object o){
return 0;
}
};
}
}
9、练习
9.1、static练习
提供属性的get,set方法
声明构造器
创建账户时,赋值
你自己对象有没有属性,才会考虑在构造器中初试化,静态的属性通常都不会在构造器中操作
造了两个账户,
package com.zhou.java;
public class AccountTest {
public static void main(String[] args) {
Account acct1= new Account();
Account acct2= new Account("qwerty",2000);
Account.setInterestRate(0.2);
Account.setMinMoney(100);
System.out.println(acct1);
System.out.println(acct2);
System.out.println(acct1.getInterestRate());
System.out.println(acct1.getMinMoney());
}
}
class Account {
private int id;
private String pwd="00000";
private double balance;
private static double interestRate;
private static double minMoney=1.0;
private static int init=1001;
public Account(){
id=init++;
}
public Account(String pwd,double balance){
id=init++;
this.pwd=pwd;
this.balance=balance;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
public static double getInterestRate() {
return interestRate;
}
public static void setInterestRate(double interestRate) {
Account.interestRate = interestRate;
}
public static double getMinMoney() {
return minMoney;
}
public static void setMinMoney(double minMoney) {
Account.minMoney = minMoney;
}
@Override
public String toString(){
return "[Account \"+\"+id=\"+id+\",pwd\"+pwd+\",balance=\"+balance+\",init\"+init]";
}
}
9.2、抽象类练习
抽象类要求子类去继承,final修饰的类是不能被继承
一个抽象类中可以定义构造器,子类还需要用构造器,还要加载属性、方法、非抽象的方法
不能造employ对象
抽象类这一块多态确实还得用
声明一个方法,方法有形参,形参写类类型,恰好类是一个抽象类,必须要写成多态
9.3、代码块练习
l举例1:
1中的先后顺序
new Leaf();,就去空参构造器,空参构造器,new 子类构造器,一上来就会去看父类怎么加载的,又去了父类有参构造器
父类有参构造器又调空参构造器
省略了super()
又再去调父类Root的空参构造器
父类Root的空参构造器又有一个super,接着再去找父类,即为Object,即先将Objetc类中的结构加载,然后再加载Root类,加载Root类,静态的相关结构就加载出来,子类Mid执行,子类leavf,先是他们的静态代码块执行
其实是想new leaf(),就涉及到构造器一个涉及到非静态代码块,就涉及到构造器或代码块谁先执行
总结:由父及子,静态先行
举例2:
main是程序入口,还是静态方法
对属性赋值有这么些位置,关注的是先后顺序是怎么样的
9.4、final练习
9.5、接口练习
10、面试题
10.1、
1.随着类的加载而加载。早于对象的创建,只要权限允许,可以通过对象.static属性的方式进行调用。存在于方法区的静态域
10.2、抽象类与接口有哪些异同?
10.4、