前置知识
开发阶段的代码在硬盘中存储,在内存中以进程的方式运行,cpu执行程序,不停的给进程分配时间片段来运行。
计算机语言将代码逻辑转换为底层的0/1使计算机识别
类
加载程序到JVM中的时机:
- 执行main函数
- 启动tomcat
创建对象
对象是根据类创建的。在Java中,使用关键字 new 来创建一个新的对象。创建对象需要以下三步:
- 声明:声明一个对象,包括对象名称和对象类型。 (1:准备一个class)
- 实例化:使用关键字 new 来创建一个对象。(2: new 去标识出来创建对象)
- 初始化:使用 new 创建对象时,会调用构造方法初始化对象。(3:会调用构造函数去java堆中申请空间)
变量
- 局部变量:在方法、构造方法或者语句块中定义的变量被称为局部变量。变量声明和初始化都是在方法中,方法结束后,变量就会自动销毁。 局部变量的生命周期:跟随方法执行结束而死亡
- 成员变量:成员变量是定义在类中,方法体之外的变量。这种变量在创建对象的时候实例化。成员变量可以被类中方法、构造方法和特定类的语句块访问。给对象创建提供了依据模板,成员变量存储在方法区的类信息中。它在创建对象new User()的时候申请空间 , 对象死亡就消亡了
- 类变量:类变量也声明在类中,方法体之外,但必须声明为 static 类型。它存储在方法区的静态区中,而且静态成员在编译的时候就存储 进去了。
静态成员和非静态成员
静态成员是放在堆中,是共享数据。所以当线程并发时容易出现数据安全问题。非静态成员及实例变量还有成员变量,他们在被使用的时候会有入栈和出栈的过程。局部变量会会在每个线程入栈的时候创建的各自的局部变量表内。
成员变量则是在创建对象,申请堆内存的时候,在每个对象的堆空间的局部变量表中
所以,通常不被太多次改变的数据将其用static修饰为静态成员。被多次改变的,不用static
继承(复用 封装 职责分担)
== 一个类有了继承,才会有多态的概念==
super
关键字就是为了给父类私有属性赋值。
继承就是为了去拿一些东西,去拓展一些东西
super
关键字就是为了给父类私有属性赋值,父类去承担一些行为逻辑。父类若没有无参构造方法,子类必须用super
显示申明- 如果在开发中,想让子类获取父类成员属性和方法的话,尽量定义为
protected
. - 如果是私有的访问权限,那就用
get
公开方法获取,用super
或者set
公开方法赋值 - 子类覆盖父类方法时,访问权限修饰符必须大于父类
多态
什么是多态?
Java语言是基于动态函数绑定的一种机制,要实现多态,必须先有继承。 当一个父类去调用一个子类方法的时候,并不能立即确定这个数据类型,需要在运行时自动确定其类型的过程就是多态
抽象类和接口最能体现多态
- Java中的任何一个类都是多态的,它们能保存不止一种类型的对象。
- 它们可以保存的是除了自身对象以外,还可以保存声明类型的子类的对象。
- 当把子类对象赋值给父类对象的引用的时候,就会引发多态。(也称之为:向上造型)。
向上造型
- 运行时如果不合理就会出现类型转换异常:
ClassCastException
- 拿一个子类的对象,当做父类的对象来用使用
- 向上造型是默认的,不需要运算符
动态绑定
当一个父类存在多个子类型的时候,parent就是一种多态变量。
parent.
这个动作执行时,java会自动去匹配实际的类型,这就是多态。
for(Parent parent: ParentList){
parent.println(); // 这种多态变量,会根据自身的数据类型,自动去匹配子类的方法
}
parent.方法
这个动态称之为:函数绑定
- 当通过对象变量调用函数的时候,调用哪个函数这个动作叫做绑定、
- 静态绑定:根据变量的声明类型来决定(也就是说你这个变量的申明类型,我就调用这个申明类型的函数。)
- 动态绑定:根据变量的动态类型来决定(也就是说:当实际管理的对象类型是什么,我就用那个类型的函数,因为编译器在编译的时候,根本不知道你这个具体的类型是什么?在java中选择都是动态绑定来处理函数的绑定。)
- 在成员函数中调用其他成员函数也可以通过this这个对象变量来调用。
比如在
parent.print
这行的时候,其实在编译的时候并不知道具体的类型是什么。所以只能在运行的时候才能确认具体的类型。对于像java这样的程序语言来说,默认所有的绑定都是:动态绑定。
抽象类(职责分担,约束子类)
抽象类只是比普通类多了一个抽象方法,作用是约束子类
- 含有
abstract
修饰符的class即为抽象类,abstract
类不能创建实例对象。- 含有abstract方法的类必须定义为
abstract class
,abstract class
类中的方法不必是抽象的。abstract class
类中定义抽象方法必须在具体(Concrete
)子类中实现,所以,不能有抽象构造方法或抽象静态方法。如果子类没有实现抽象父类中的所有抽象方法,那么子类也必须定义为abstract
类型。
为什么会存在抽象类?
- 因为能够让子类重写的方法,大部分情况父类是不需要去做事情,也就是必须让子类自身去实现。普通的父类是起不到约束的作用,也就是说,父类中需要覆盖的方法和普通方法这个时候就难以分辨和区分。所以就有了抽象方法abstract。接口更加将此体现的淋漓尽致。
- 抽象类可以定义抽象方法和非抽象方法。==那么父类中的非抽象方法的意义是什么呢?==其实还是回归到继承的概念:职责分担,大部分父类的非抽象方法就是去分担子类的业务,这样便于后续的维护和升级,这样就不需要去修改每个子类。只需要修改父类的方法,当然前提是方法的访问权限必须是:public或者protected的,如果是缺省的就必须在同包package中。
- 抽象类,在平时的业务中那些场景下可以用到抽象类呢?
- 比如springmvc路由的继承
- hibernate、jdbctempalte中的通用类的增删改查
- spring框架中存在大量的抽象类,其意义就是:责任分担。
抽象类抽取公用的东西同一管理(pringmvc
中路由的继承就有用到)
@RequestMapper("/api/v1")
@Controller
public abstract class BaseController(){
//建立这样一个抽象类作为基础控制器
//那么继承它的控制器路径最前面是"/api/v1"的就可以省略
}
建立这样一个抽象类作为基础控制器
那么继承它的控制器路径最前面是"/api/v1"的就可以省略
而当我们迭代更新
再比如抽象类中用
protected
修饰公共方法供子类继承,这样子类在需要啥的时候就不能用注入了
其它
- 抽象类不可以被实例化
由实现它的子类去实例化
- 抽象类有构造函数,但是不能实例化,那么构造函数的意义是什么
在子类构造函数中通过super方法给抽象类的私有属性赋值
- 抽象类A继承抽象类B,必须要覆盖父类抽象方法吗?
不用,但如果class C 为最终实现类继承A,则必须实现A和B中所有抽象方法。但是如果不想实现B中的抽象方法,这种情况用抽象类不可以,所以有了接口,jdk1.8后有默认方法
接口(规范约束)
java单继承,多实现
抽象类中定义的抽象方法在子类中必须要实现,并且还不能同时继承多个类。这无疑带来了很多麻烦。所以出现了接口
接口是一种特殊的抽象类
- 接口中的方法默认全是抽象方法
public abstract
(可省略) - 接口中成员变量默认都是静态常量
public static final
(可省略) - jdk1.8以后允许默认方法和静态方法
抽象类只能单继承,方法必须实现
接口多实现,可同时实现多个接口,不想实现的抽象方法用default
修饰加方法体{}
即可
default void m1(){
system.out.print(“接口不实现的方法体”)
}//根据开发需求选择实现
但是,如果此时接口为多实现的话且不同接口有相同的
default
方法,则实现类必须重写该方法,应为子类不知道该继承哪个接口的方法
抽象类中的非抽象方法可以分担子类的逻辑行为,接口中也有类似的静态方法来分担职责,在子类中使用接口调用,
接口与抽象类
1.抽象类可以有构造方法,接口中不能有构造方法。
2.抽象类中可以有普通成员变量,接口中没有普通成员变量
3.抽象类中可以包含非抽象的普通方法,接口中的所有方法必须都是抽象的,不能有非抽象的普通方法
4.抽象类中的抽象方法的访问类型可以是public
,protected
和默认类型,但接口中的抽象方法只能是public
类型的,并且默认即为public abstract
类型。
5.抽象类中可以包含静态方法,接口中不能包含静态方法,JDK1.8以后允许
6.抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以任意,但 接口中定义的变量只能是public static final
类型,并且默认即为public static final类型。
7.一个类可以实现多个接口,但只能继承一个抽象类。 单一继承多实现
8.Jdk1.8以后新增了默认方法default和static方法,慢慢替代抽象类的的含义,但是不能完全的消除抽象类。
抽象类中的非抽象方法更多时候起的作用是帮子类分担职责
而接口中类似的则有静态方法static void m2(){ return 1; }
抽象类可以实现接口,接口可以继承多接口。
接口其实也可以new
,但是此时它必须自己重写实现自己
枚举(自产自销)
枚举在统一返回与统一异常处理的场景中很适合使用
public enum ConstantsEnum {
SESSION_USER,COOKIE_USER;
}
这个枚举代码相当于一个类自己new自己:
public class ConstantsEnum {
ConstantsEnum SESSION_USER = new ConstantsEnum();
ConstantsEnum COOKIE_USER = new ConstantsEnum();
public ConstantsEnum(){
}
}
- 枚举是一个特殊类,可以定义成员变量,定义构造函数
SESSION_USER
,COOKIE_USER
其实是一个枚举引用,调用枚举类中隐藏的无参构造方法- 枚举不能继承类,不能被new,因为它是自产自销(默认private)
- 枚举修饰符是
private
,因为jdk把枚举定义成了一个单例模式 - 可自定义普通方法,能实现接口,对其行为约束,可自定义普通方法
- 当使用有参构造方法覆盖无参时,
SESSION_USER
,COOKIE_USER
也必须加上参数SESSION_USER(session_user)
,COOKIE_USER(cookie_user)