java面向对象
即使再小的帆也能远航~
一.目录
👨?👨?👧?👦类和对象
👨?👧?👦构造方法
👨?👩?👧this关键字
👨?👧方法的参数值传递
👨?💼成员变量,局部变量
👶静态变量,静态方法
?代码块
🚑权限修饰符
???♀?封装
👰继承
😥多态
??单例模式
🛰final修饰符
??抽象类
💱接口
🦅内部类
🌍Lambda表达式
🤡关联/依赖关系
🎄模板模式
🚖工厂模式
🥇策略模式
💠包装类
🔌枚举类
二.内容
1.1面向对象的概念
这里的对象泛指现实中一切事物,每种事物都具备自己的属性和行为。
面向对象程序设计思想就是在计算机程序设计过程中,参照现实中事物,将事物的属性特征、行为特征抽象出来,描述成计算机事件的设计思想。
1.2面向对象与面向过程
面向对象:强调对象,通过调用对象的行为来实现功能
例:把衣服脱下来–>打开全自动洗衣机–>扔衣服–>按钮–>晾起来
面向过程:强调步骤,该如何一步一步去实现
例:把衣服脱下来–>找一个盆–>放点洗衣粉–>加点水–>浸泡10分钟–>揉一揉–>清洗衣服–>拧干–>晾起来
1.3类
●类:类是一组相关属性信息和动作行为的集合,可以看成是一类事物的抽象描述,使用事物的属性特征和行为特征来描述该类事物,仅仅是概念上的定义。
在Java中使用类来描述现实世界中真实存在的一切事物:
●属性:就是该事物的状态信息。
●行为:就是该事物能够做什么
类是一种新的引用数据类型(数据类型就能创建该类型的变量,创建该类型的数据).
注:一般会认为整数小数之类的才是数据,而引用类型的数据,比如:一个扫描器,一个学生,一 个鸟既具有行为,又具有特征,在java中特殊称呼为对象
1.4对象
对象:对象是一类事物的个体体现。对象是实际存在的该类事物的一个个体,因此也被称为实例,必然具备该类事物的属性和行为。
1.5类与对象的关系
●类是对一类事物的描述,是抽象的,概念上的定义。
●对象是一类事物的个体体现,是具体的真实存在的。
●类的实例化就是对象,对象的抽象化(图纸)就是类。类是对象的模板,对象是类的实体。
1.6JVM内存划分
任何程序运行,都必须获得操作系统分配的内存
java系统对获得的内存又做了划分,提高使用效率
-
栈内存 内存小,如同人身体上大脑,做运算,执行调用等等. FILO:先进后出 , “ 井 ” 存放 局部变量 (声明在方法内部的变量),栈中的变量没有默认值! 需要初始化!!! 局部运行完就释放了! -
堆内存 内存大,如同四肢躯干不做运算,做仓库使用,等待 栈中的 变量的引用 , 存放 引用类型的数据, 绝大多数new出来的内存都是申请的堆里的内存!!! 在堆中的 引用数据 都具有默认值!!! 整数 : 0 , 0L 小数: 0F 0 char: ‘ ’ 空格字符 boolean: false 引用类型: null 引用数据: 必须变为内存垃圾 之后 .等待 JVM的垃圾回收线程 空闲时 来释放!!! -
方法区内存 存放常量 和 类的字节码文件 (XXX.class),static修饰的变量 里面的数据: 只要创建出来 就一直存活到JVM停止!!!
- 本地方法区 java系统和OS之间使用的
- 本地寄存器 java系统和OS之间使用的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vZX2Wjph-1633327904391)(http://m.qpic.cn/psc?/V53NcVjF2R8Gj53iIrB43E3dPm3SCuMN/45NBuzDIW489QBoVep5mcaHNU4kNXyspZcm.V6jhAOT8SYuDcpH9VRqdAi9SduLhNkwiK.JK1qxPVEhPbIjumGxydrfC53TaEgTsDay6.aU!/b&bo=IANGAQAAAAAAF1U!&rf=viewer_4)]
1.7创建对象原理
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jUt942On-1633327904396)(http://m.qpic.cn/psc?/V53NcVjF2R8Gj53iIrB43E3dPm3SCuMN/45NBuzDIW489QBoVep5mcfYdAKHfZywYD9Vhc6TKZmFf1IkHEFCtrbA96LI3nxKLxo0Hfp6ODnuYB3mmwdd*7qvzlYn0NTjgMCGffjlTou4!/b&bo=IAOUAQAAAAAAF4c!&rf=viewer_4)]
2.1概念
- 构造方法又被称为构造器,作用就是对通过该类的创建的实例对象进行初始化操作,Java语言使用new关键字来调用构造方法来创建这个类的实例。
- 构造方法是一个类创建对象的根本,如果一个类没有构造方法,那么Java就会为该类提供一个默认的无参构造方法。接口不允许被实例化,所以接口中没有构造方法.
- 构造方法在初始化对象时自动执行,一般不能显式地直接调用.构造方法的调用是在创建一个对象时使用new操作进行的
2.2语法格式
[修饰符]构造器名(形参列表){
}
- 注意:构造器既不能定义返回值类型,也不能使用void定义构造器没有返回值。这是Java规范定义的,实际上类的**构造器是有返回值的,当使用new关键字来调用构造器时,构造器返回该类的的实例。**这是因为根据不同的类创建的对象类型也不同,所以无法确定返回该类型是什么,所以就不定义了,也可以把构造方法名看成该构造方法的返回类型。
- 修饰符:可以省略,但是尽量不要省略,尽可能的从public、protected、private中选择一个
- 构造器名:必须和类名保持一致。
2.3构造方法作用:
(1)构造出来一个类的实例 (实例化对象)
(2)对构造出来的一个类的实例(对象)初始化
2.4构造方法分类
- 无参构造
不会给成员变量赋值(初始化对象) - 有参构造
给成员变量赋值(初始化对象)
注:
- 如果一个类中没有手动添加构造方法,java系统将会提供一个默认的无参构造.
- 如果我们在一个类中设计了一个有参构造方法,java系统将不再提供无参构造,若想使用无参构造,需要自己添加.
2.5快捷键
2.6构造方法重载
同一个类里具有多个构造器,多个构造器的形参列表不同,即被称为构造器重载。(即:方法名相同,参数列表不同)
public class Person {
String name;
int age;
public Person (String name){
this.name=name;
}
public Person (String name,int age){
this.name=name;
this.age=age;
}
this关键字主要有三个应用:
- this调用本类中的属性,也就是类中的成员变量
- this调用本类中的其他方法;
- this调用本类中的其他构造方法,调用时要放在构造方法的首行。
3.1调用成员变量(“this.成员变量”)
public class Student{
String name;
private void SetName(String name){
this.name = name;
}
}
- 这段代码中,创建了一个类Student,有成员变量name与成员方法SetName(String name),由于成员方法接收的形参名称与成员变量相同,都是name,所以,这里可以使用this关键字来调用本类中的成员变量。其作用可以简单的这么理解:this关键字就是调用本类中的成员变量。
- 但是这样简单的理解并不准确,this其实是当前类对象的引用,通过当前这个类实例化的对象的引用来访问对象的成员变量。
3.2调用成员方法(“this.方法名()”)
package Test;
public class ThisTest {
String name;
private void setName(String name) {
this.name = name;
System.out.println("setName: " + this.name);
}
private void thisTest(String name) {
this.setName(name);
System.out.println("thisTest: " + this.name);
}
public static void main(String[] args) {
ThisTest tt = new ThisTest();
tt.setName("Yida");
tt.thisTest("Jenny");
}
}
this关键字用在成员方法内,指示当前对象,(当前对象:来调用方法的那个对象),此时this可以省略!
3.3调用构造方法(“this(参数)”)
构造方法是一个类的对象在通过new关键字创建时自动调用的,在程序中不能向调用其他方法一样通过方法名(也就是类名)来调用。但如果一个类有多个构造方法,可以在一个构造方法中通过this(paras…)来调用其他的构造方法。
使用this来调用其他构造方法有如下几个约束:
- 只能在构造方法中通过this来调用其他构造方法,普通方法中不能使用。
- 不能通过this递归调用构造方法,即不能在一个构造方法中通过this直接或间接调用该构造方法本身。
- 通过this调用其他构造方法必须放在构造方法的第一行中执行。
- 由于super调用父类的构造函数也必须放在构造方法的第一行中执行,因此,通过this和super调用构造方法不能同时出现一个构造方法中。也不能在一个构造方法中多次调用不同的构造方法。
public class Student {
public Student() {
this(“Hello!”);
}
public Student(String name) {
}
}
3.4返回对象的值
this关键字除了可以引用变量或者成员方法之外,还有一个重大的作用就是返回类的引用。如在代码中,可以使用return this,来返回某个类的引用。此时这个this关键字就代表类的名称。如代码在上面student类中,那么代码代表的含义就是return student。可见,这个this关键字除了可以引用变量或者成员方法之外,还可以作为类的返回值,这才是this关键字最引人注意的地方。
注:main方法调用一个方法的时候,该方法会进栈,因为只有栈内存才能运行和计算
-
参数类型是基本类型 . 外界调用方法时,传入的实参是基本类型的数据 的拷贝/复制.方法里的操作不会改变参数的值 public class Student {
String name;
public void change(int a){
a=108;
}
}
class StudentTest{
public static void main(String[] args) {
Student stu = new Student();
int a=3;
System.out.println("调用方法之前:a="+a);
stu.change(a);
System.out.println("调用方法之后:a="+a);
}
}
调用方法之前:a=3
调用方法之后:a=3
Process finished with exit code 0
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o5mBgRwG-1633327904404)(http://m.qpic.cn/psc?/V53NcVjF2R8Gj53iIrB43E3dPm3SCuMN/45NBuzDIW489QBoVep5mcV1bmHhs0QIqN8SZrqbwRe.PZy92nIXmT87MIyvdNza5ru76EOXuJSu4jL9RQwUhnid64.1QhQe1Kpq0RAKPd0!/b&bo=5wYqAwAAAAADFo!&rf=viewer_4)] -
参数类型是引用类型 . 外界调用方法时,传入的实参是引用类型数据(对象)的地址的拷贝!方法里的操作会改变参数的值
public class Student {
String name;
public void changeName(Student student){
student.name="gxxt";
}
public Student(String name) {
this.name = name;
}
}
class StudentTest{
public static void main(String[] args) {
Student stu1 = new Student("gy");
System.out.println("调用方法之前的名字:"+stu1.name);
stu1.changeName(stu1);
System.out.println("调用方法之后的名字:"+stu1.name);
}
调用方法之前的名字:gy
调用方法之后的名字:gxxt
Process finished with exit code 0
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QmyehU8T-1633327904412)(http://m.qpic.cn/psc?/V53NcVjF2R8Gj53iIrB43E3dPm3SCuMN/45NBuzDIW489QBoVep5mcUpcKtpVZjNkDTXPAHHlAzShJDp3c0Nuv5AEi.D8SZm3z9*tqWe.z7tWkU7ubwif0zuZI3ksh94O.NwA7fOmkj0!/b&bo=7AYxAwAAAAADF.o!&rf=viewer_4)]
变量根据定义位置的不同,我们给变量起了不同的名字。如下图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gvYAFewW-1633327904418)(http://m.qpic.cn/psc?/V53NcVjF2R8Gj53iIrB43E3dPm3SCuMN/ruAMsa53pVQWN7FLK88i5q146YYKPOu2GNL24D7bka38WLT33r3D8jOX63Emq2bkXeoY2PFiT5hWUHbIUggNLs.gEzfKR1chf6xrg2fHE0E!/mnull&bo=UwIGAQAAAAADB3Q!&rf=photolist&t=5)]
●在类中的位置不同 重点
○成员变量:类中,方法外
○局部变量:方法中或者方法声明上(形式参数)
●作用范围不一样 重点
○成员变量:类中
○局部变量:方法中
●初始化值的不同 重点
○成员变量:有默认值
○局部变量:没有默认值。必须先定义,赋值,最后使用
●在内存中的位置不同 了解
○成员变量:堆内存
○局部变量:栈内存
●生命周期不同 了解
○成员变量:随着对象的创建而存在,随着对象的消失而消失
○局部变量:随着方法的调用而存在,随着方法的调用完毕而消失
- 局部变量和成员变量重名时
当局部变量和成员变量重名时,方法会就近使用自己的局部变量!!!
6.1static修饰的成员变量 变为 类变量 属于 类的特征!!!
- 不是每个对象都有一套.而是共享的! 存在于类的字节码文件所在对的方法区内存!!!就一份!!! (成员变量 是 每个对象都有自己的一套,对象间互不干涉!)
- 类变量 可以直接使用 类名.变量名 去使用 可以使用对象.变量名 去使用 推荐 类名点儿去使用
- 类变量 会 跟随 类文件的加载(字节码文件进入方法区内存) 而被 创建到 方法区内存中!
| 局部变量 | 成员变量 | 类变量 |
---|
位置 | 方法参数 方法内部声明 各种语句if for … 局部代码块内声明 | 类中方法外 没有static修饰的变量 | 类中方法外 有static修饰的变量 | 作用 | 存放数据 | 标识对象的特征 每个对象都有一套 | 标识类的特征 所有对象共享一个 | 内存 | 跟随局部在栈中 | 跟随对象在堆中 | 跟随类在方法区 | 生命周期 | 自声明到所在局部结束 | 跟随对象 对象.成员变量 来使用 对象变为垃圾被回收 | 跟随类的字节码文件 直到JVM停止 | 使用 | 直接变量名使用 先声明后初始化才能使用 同一个局部不能有同名变量 | 必须 对象.成员变量 堆中有默认值 | 推荐 类.类变量 对象.类变量 也可以 方法区变量也有默认值 |
6.2static 修饰的成员方法 变为 类方法 属于类的行为!!!
修饰符 static 返回值类型 方法名(){
}
- static方法 可以直接 使用 类名.方法名 来使用 , 可以对象.方法名来使用 不推荐!
- 由于 静态方法 不需要对象 就能够 调用 . 很多 工具类性质的 方法 都 采用static来修饰!!!因为这样可以 省下一句 new 对象的代码.
- 类型A的静态方法内部 ,只能调用类型A的静态变量 或 静态方法 , 不能调用 成员变量 或 成员方法.(静态方法内不能有非静态的东西) 原因: static设计目的: 不需要对象!!!
- 成员方法内部: 能调用 成员变量 或 成员方法 , 也可以调用 类变量 或 类方法!
7.1构造代码块
- 构造代码块:用在创建对象时给成员变量赋值!
- 执行一些代码,每创建一次对象,这里就运行一次!
- 作用和构造方法一样,几乎不使用!
- 时机早于构造方法
public class Employee {
String name;
int age;
{
System.out.println("构造代码块执行了~");
this.name="丁真";
}
public Employee() {
System.out.println("无参构造执行了~");
}
public Employee(String name, int age) {
this.name = name;
this.age = age;
System.out.println("两参构造执行了");
}
}
class EmployeeTest{
public static void main(String[] args) {
Employee e1= new Employee();
System.out.println(e1.name);
}
}
构造代码块执行了~
无参构造执行了~
丁真
Process finished with exit code 0
7.2静态代码块
static 修饰的代码块 是 静态代码块 类代码块
| 局部代码块 | 构造代码块 | 静态代码块 ★★★★★ |
---|
位置 | 局部(方法或语句本身或内部的) | 类内,方法外,单独的{} | 类内,方法外,单独的{} 带有static修饰 | 作用 | 限制局部,尽早的释放局部变量 | 作用同构造方法 时机早于构造方法 | 作用:在类加载时执行一些代码 通常是来加载外部配置文件 | | | | |
package OOP;
public class Employee {
String name;
int age;
static int num =1314521;
static {
System.out.println("跟随类的加载而运行一次!比如:读取文档内存或配置文件");
}
public Employee() {
}
}
class EmployeeTest{
public static void main(String[] args) {
Employee e1= new Employee();
System.out.println(Employee.num);
}
}
public class Employee {
String name;
int age;
static int num =1314521;
static {
System.out.println("跟随类的加载而运行一次!比如:读取文档内存或配置文件");
}
{
System.out.println("构造代码块执行了~");
this.name="丁真";
}
public Employee() {
System.out.println("无参构造执行了~");
}
public Employee(String name, int age) {
this.name = name;
this.age = age;
System.out.println("两参构造执行了");
}
}
class EmployeeTest{
public static void main(String[] args) {
System.out.println(Employee.num);
Employee e1= new Employee();
Employee e2= new Employee("gy",18);
}
}
跟随类的加载而运行一次!比如:读取文档内存或配置文件
1314521
构造代码块执行了~
无参构造执行了~
构造代码块执行了~
两参构造执行了
Process finished with exit code 0
由上述示例代码得:
- 每创建一次对象,构造代码块就运行一次!
- 执行顺序:静态代码块>构造代码块>无参构造>有参构造
在Java中提供了四种访问权限,使用不同的访问权限修饰符修饰时,被修饰的内容会有不同的访问权限,
●public:公共的;
●protected:受保护的;
● :默认的;
●private:私有的;
不同权限的访问能力
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Xv72xOWr-1633327904420)(http://m.qpic.cn/psc?/V53NcVjF2R8Gj53iIrB43E3dPm3SCuMN/45NBuzDIW489QBoVep5mcQ0UpERnenoC55vXxYyPpUUorfXHlh6fAkLg5dGHF7CMS4D3r.tnwZRSwUnT4FJodoGx.wZdvjZ7.vn27b37c!/b&bo=ZwQkAQAAAAADF3Q!&rf=viewer_4)]
9.1封装的作用:
封装把一个对象的属性私有化,同时提供一些可以被外界访问的属性的方法,如果属性不想被外界访问,我们大可不必提供方法给外界访问。但是如果一个类没有提供给外界访问的方法,那么这个类也没有什么意义了。
说成大白话就是防止别人更改你的变量
9.2为什么要使用封装?
封装符合面向对象设计原则的第一条:单一性原则,一个类把自己该做的事情封装起来,而不是暴露给其他类去处理,当内部的逻辑发生变化时,外部调用不用因此而修改,他们只调用开放的接口,而不用去关心内部的实现
1、比如打电话,我们只要按键拨打就可以,不需要知道手机的内部结构,也不需要知道怎么打出去 2、比如给手机充电,只要通过手机提供的接口,就可以,不需要知道怎么把电冲进去的
9.3封装的步骤
1、使用private关键字来修饰成员变量。
2、对需要访问的成员变量,提供对应的一对getXxx方法、setXxx方法(快捷键生成)。
9.4拓展
封装类又叫数据模型类/实体类/javabean
10.1 概念
Java的类可以分为三类:
- 类:使用class定义,没有抽象方法
- 抽象类:使用abstract class定义,可以有也可以没有抽象方法
- 接口:使用inerface定义,只能有抽象方法
- 继承是面向对象的又一大特征,也是实现代码重用的重要手段。
- 父类又称为超类或者基类。而子类又称为派生类,而且java只支持单继承,可以多层次继承(父,祖父,曾祖父,)
- 继承体系里. 父类表示一般的,通用的,基础的, 所以父类也称 基类.
子类表示具体的,特殊的,新颖的, 子类可以扩展父类, 创建新成员变量 和 成员方法 , 以及重写父类的成员方法.
10.2格式
class A extends B{
}
10.3 super关键字
成员变量不重名
如果子类父类中成员变量不重名,这时的访问是没有影响的。
class Father {
int num=5;
}
class Child extends Father {
int num2=6;
public void show(){
System.out.println("Fathernum="+num);
System.out.println("Childnum2="+num2);
}
}
class Demo {
public static void main (String[] args) {
Child z =new Child();
z.show();
}
}
成员变量重名
如果子类父类中出现重名的成员变量,这时的访问是有影响的。代码如下:
class Father {
int num=5;
}
class Child extends Father {
int num=6;
public void show() {
System.out.println("Father num="+num);
System.out.println("Child num="+num);
}
}
class Demo {
public static void main (String[] args){
Child z= new Child();
z.show();
}
}
Father num=6
Child num=6
Process finished with exit code 0
子父类中出现了同名的成员变量时,在子类中需要访问父类中非私有成员变量时,需要使用super关键字,修饰父类成员变量,类似于之前学过的this。
使用格式:
super.父类成员变量名
代码修改如下:
class Father {
int num=5;
}
class Child extends Father {
int num=6;
public void show() {
System.out.println("Father num="+super.num);
System.out.println("Child num="+num);
}
}
class Demo {
public static void main (String[] args){
Child z= new Child();
z.show();
}
}
super关键字用在成员方法里面
super指代父类的引用
- super.成员变量
调用 父类的成员变量 - super.成员方法
调用 父类的成员方法
super关键字用在构造方法里面
1、构造方法的名字是与类名一致的。所以子类是无法继承父类构造方法的。
2、构造方法的作用是初始化成员变量的。所以子类的初始化过程中,必须先执行父类的初始化动作。
子类的构造方法中默认有一个super(),表示调用父类的构造方法,父类成员变量初始化后,才可以给子类使用。
- super()或者super(参数)//必须写在子类构造方法的第一行!!!
- 调用父类的构造方法
注:
- 子类对象调用构造的时候,必须先调用父类的构造,默认是无参的!!!
- 当然,可以在子类的构造中使用super(参数)指定具体调用父类的哪个有参构造
10.4方法重写(override)
子类与父类方法名一样,参数列表一样.
重写: 要求 子类的方法声明和父类的一致. 方法体不一致. 新设计功能!
| 重载 | 重写 |
---|
位置 | 同一个类中 | 继承关系的子类里 | 方法名 | 相同 | 相同 | 访问修饰符 | 无关 | 子类的>=父类的 | 返回值 | 无关 | 相同 | 参数列表 | 必须不一致 | 相同 | 方法体 | 无关 | 理论上是不一样 | 异常列表 | 无关 | 子类<=父类的 , 少于等于父类的 |
10.5继承关系里子类对象的创建过程
平常不用,为了面试笔试做准备!!!
public class Father {
static {
System.out.println("父类的静态代码块");
}
{
System.out.println(" 父类的构造代码块");
}
public Father(){
System.out.println("父类的无参构造");
}
}
class Child extends Father {
{
System.out.println("子类的构造代码块");
}
public Child() {
System.out.println("子类的无参构造");
}
static {
System.out.println("子类的静态代码块");
}
}
class Demo {
public static void main(String[] args) {
Child child = new Child();
}
}
父类的静态代码块
子类的静态代码块
父类的构造代码块
父类的无参构造
子类的构造代码块
子类的无参构造
Process finished with exit code 0
结论:记住创建的顺序:
- 父类的静态代码块
- 子类的静态代码块
- 父类的构造代码块
- 父类的无参构造
- 子类的构造代码块
- 子类的无参构造
原因:子类对象的创建过程: 子类的构造里 默认 会 调用 父类的无参构造! 或 自己指定的父类的有参构造!
11.1多态的概念
例:跑的动作,小猫、小狗和大象,跑起来是不一样的。再比如飞的动作,昆虫、鸟类和飞机,飞起来也是不一样的。
Java引用变量有两个类型,一个是编译时类型(左边),一个是运行时类型(右边)。编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。如果编译时类型和运行时类型不一致,就可能出现多态。所谓的多态就是同样一个东西表现出多种不同方式。
父类类型 变量名= new 子类对象;
变量名.方法名();
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,执行的是子类重写后方法。
11.2多态实现的条件
- 有继承关系/接口实现implements关系
- 子类重写父类方法
- 上塑造性/向上转型
11.3引用类型的强制类型转换
编写Java程序时,引用变量只能调用它编译时类型的方法,而不能调用它运行时类型的方法,也就是说,利用多态创建的对象,只能调用父类拥有的方法,而不能调用子类特有的方法,.即使它实际引用的对象确实包含该方法,也无法引用到。如果需要让这个引用变量调用它运行时类型的方法,则必须把它强制转换成运行时类型,强制类型转换需要借助于类型转换运算符。
多态的转型分为向上转型与向下转型两种:
向上转型
多态本身是子类类型向父类类型向上转换的过程,这个过程是默认(多态就是向上转型)的。当父类引用指向一个子类对象时,便是向上转型。
Java允许把一个子类对象直接赋值给父类引用变量,无须任何类型转换。或者称为向上转型,向上转型由系统自动完成。
使用格式:
父类类型 变量名 =new 子类类型();
如:Animal a=new Cat();
向下转型(做法同基本数据类型的强制转换相同)
父类类型向子类类型的转换就是向下转型的过程,这个过程是强制的。一个已经向上转型的子类对象,将父类引用转为子类引用,可以使用强制类型转换的格式,便是向下转型。
子类类型 变量名=(子类类型) 父类变量名;
如:Cat c=(Cat) a;
利用多态创建的对象,只能调用父类拥有的方法,而不能调用子类特有的方法,如果想调用==>向下转型
instanceof运算符
问题引入
父类Animal,子类有Bear,Cat,Dog
多态:Animal a= new Cat(); //向上转型
向下转型:Bear b=(bear) a;
==>a向下转型为Bear类型,会报错:ClassCastException(类型转换异常)
格式
为了避免ClassCastException的发生,Java提供了instanceof关键字,给引用变量做类型的校验,格式如下:
变量名 instanceof 数据类型
如果变量属于该数据类型,返回true。如果变量不属于该数据类型,返回false。
public static void main (String[] args){
Animal animal=new Cat();
if ( animal instanceof Cat) {
Cat cat =(Cat) animal;
}
}
12.1饿汉式
步骤
- 私有化构造
- 提供唯一的静态的该类型对象
- 封装的做法:提供公共访问/获取唯一对象的方法
示例代码:
public class SingletonDemo {
String name;
private SingletonDemo(String name) {
this.name = name;
}
private SingletonDemo() {
}
private static SingletonDemo instance=new SingletonDemo();
public static SingletonDemo getInstance(){
return instance;
}
}
class SingletonTest{
public static void main(String[] args) {
SingletonDemo instance = SingletonDemo.getInstance();
}
}
饿汉式中本类唯一对象的创建时机:类文件加载的时候,导致比较早的出现到内存中**(static 修饰的变量会随着类文件的加载而被加载)**=>占内存(方法区内存中的东西直到虚拟机停止才会被释放)
12.2懒汉式
懒汉式对象创建的时机:外界第一次获取该类对象时!
优点:不占内存
缺点:多线程情况下有可能出现线程不同步问题!!
public class SingletonDemo {
String name;
private SingletonDemo(String name) {
this.name = name;
}
private SingletonDemo() {
}
private static SingletonDemo instance;
public static SingletonDemo getInstance(){
if (instance==null){
instance =new SingletonDemo();
}
return instance;
}
}
class SingletonTest{
public static void main(String[] args) {
SingletonDemo instance = SingletonDemo.getInstance();
}
}
13.1final成员变量
final 修饰的成员变量必须显式的进行初始化赋值来指定初始值 ,否则默认值的是个无效值 ,会报“可能尚未初始化变量xxx ”的错误。
Class Demo{
final int num ;
final int num = 10;
public Demo(){
}
}
Class Demo{
final int num;
public Demo(){
num = 10;
}
}
- 为什么final修饰的成员变量必须显式的进行初始化呢?
对于final 修饰的成员变量而言,一旦有了初始化值,就不能被重新赋值 。如果既没有 在定义 成员变量时指定初始值 ,也没有在初始化、构造器中 为成员变量指定 初始值,那么这些成员变量的值就会一直是系统默认值(成员变量在堆内存中,有默认值)
public class FinalVariableTest
{
final int a = 6;
final String str;
final int c;
final static double d;
{
str = "Hello";
}
static
{
d = 5.6;
}
public FinalVariableTest()
{
c = 5;
}
public void changeFinal()
{
}
public static void main(String[] args)
{
FinalVariableTest ft = new FinalVariableTest();
System.out.println(ft.a);
System.out.println(ft.c);
System.out.println(ft.d);
}
}
13.2final局部变量
-
系统不会对局部变量就行初始化,局部变量必须由程序员显式初始化 -
如果final局部变量在定义时没有指定初始值,则可以在后面代码中对其赋初值,但只能一次 ,不能重复赋值。 -
如果在定义时已经指定 初始值,那么在后面代码中不能再 对该变量赋值。 -
final修饰形参,形参在调用该方法时,由系统根据传入的参数来完成初始化 ,因此使用final修饰的形参不能再被赋值。
public void test(final int a)
{
}
13.3final基本数据类型变量
数据不能变
final int a=100;
13.4final引用数据类型变量
final修饰的引用数据类型变量:地址不能发生改变(堆内存里new的那块区域),但是地址所指向的内存里的成员变量的值可以更改
13.3final方法
-
不希望子类重写父类的某个方法 ,则可以使用final修饰该方法 ,如果子类试图重写该方法,就会引发编译错误 。 -
对于使用private修饰的方法,表示私有方法,只能是对象本身调用,那么子类在继承时是无法访问该方法的,所以子类无法重写该方法。即使使用final修饰一个private 访问权限的方法,依然可以在其子类中定义与该方法具有相同方法名、相同形参列表、相同返回值类型的方法 。该方法不是重写 ,只是子类中的方法定义与其恰巧相同。 public class PrivateFinalTest{
private final void test(){}
}
class Sub extends PrivateFinalTest{
public void test(){}
}
-
final方法仅仅 是不能被重写 ,可以被重载 。
13.4final类
- final修饰的类**
不可以有子类,不能被继承 **。final类试图被其他类继承,那么会发生编译错误 。 - 为了安全因素,保证某个类不可被继承,则可以使用final修饰这个类。
不可变类:(最重要前两点)
- 类型被final修饰,public final class{}
- 成员变量也被final修饰
- 方法也被final修饰
当编写一个类时,常常会为该类定义一些方法,这些方法用于描述这个类的行为。但在某些情况下只需要定义出一些方法,而不需要具体的去实现这些行为。也就是说这些方法没有方法体,只是一些签名而已,这样的方法被称为抽象方法,包含抽象方法的类被称为抽象类。
14.1抽象方法与抽象类
抽象方法与抽象类必须使用abstract关键字进行修饰,有抽象方法的类必须被定义成抽象类,抽象类里面可以没有抽象方法。
抽象类与抽象方法的规则如下:
- 抽象类与抽象方法必须使用abstract关键字进行修饰,抽象方法不能有方法体。
- 抽象类不能被实例化。即使抽象类不包含抽象方法,也不能被实例化
- 抽象类可以包含field、方法、构造器、初始化块、内部类5种成分。
- 包含抽象方法的类,只能被定义成抽象类。
语法格式:抽象方法
【修饰符】 abstract 返回值类型 方法名(形参列表);
public abstract void runWay();
注意:抽象方法是没有方法体,仅仅是一个方法声明而已。所有Java要求其子类必须将父类中定义的抽象方法进行实现(覆盖重写)。这也就意味着子类需要先将该方法继承过来,所以修饰符也就只能是public或者protected。
示例代码:抽象类定义
public abstract class Piece{}
注意:抽象类不能实例化,只能被继承,抽象类中可以没有抽象方法,即使抽象类中没有抽象方法也不能被实例化。
14.2抽象类的使用
抽象类不能创建实例,只能当成父类来被继承。抽象类可以看成是从多个具体类中抽象出来的父类,它具有更高层次的抽象。从多个具有相同特征的类中抽象出来的一个抽象类,以这个抽象类作为其子类的模板,从而避免子类设计的随意性。
抽象类的体现就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会保留抽象类的行为方式。
编写一个抽象父类,父类提供了多个子类的通用方法,并把一个或多个方法留给其子类实现,这就是一种模板模式,模板模式也是十分常见的设计模式。 模板模式在面向对象的软件中很常用,其原理简单,实现也很简单。使用模板模式有如下规则:抽象父类可以只定义需要使用的方法,把不能实现的部分抽象成抽象方法留给子类去实现。
抽象类是从多个类中抽象出来的模板,如果将这种抽象进行得更彻底,则可以提炼出一种更加特殊的“抽象类”——接口(interface)。接口里不能包含普通方法,接口里的所有方法都是抽象方法。接口里面可以放:常量,抽象方法,默认方法,静态方法,私有化方法
15.1接口的概念
Java中的接口是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)。
15.2接口的定义
接口和类定义不同,定义接口不在使用class关键字,而是使用interface关键字。
语法格式如下:
【public】interface 接口名 extends 父接口1,父接口2{
}
一个接口可以有多个直接父接口,但接口只能继承接口,不能继承类(单继承,多实现)
15.3接口中的成员变量
field只能是常量。又因为field只能是常量,所以系统自动为这些field增加了static和final两个修饰符。也就是说在接口中定义的Field不管是否使用了public static final修饰符,接口里的Field总是默认使用public static final修饰符来进行修饰,不可更改。
示例代码
interface DBobjectType{
public static final int ROOT=0;
public static final int DATABASE=1;
int TABLE=2;
int COLUMN=3;
int INDEX=4;
}
15.4接口中的方法
接口里定义的方法都是抽象方法,因此系统会自动为方法增加public abstract修饰符。因此不管定义接口方法时是否使用了public abstract修饰符,系统都会默认方法使用public abstract修饰符来进行修饰。
示例代码:
public interface DataConnection{
public abstract void getConnection();
void close();
}
15.5接口的继承
接口的继承与类的继承不一样,接口完全支持多继承(接口与接口之间),即一个接口可以有多个直接父接口。和继承相似,子接口扩展父接口,将会获得父接口里定义的所有抽象方法、field、内部类和枚举定义。
一个接口继承多个父接口时,多个父接口排在extends关键字之后,多个父接口之间使用英文逗号(,)进行分隔。
示例代码:
public interface InterA{
void a();
}
public interface InterB{
void b();
}
public interface Inter extends InterA,InterB{
voidc();
}
15.6接口的实现/使用
接口不能用于创建实例,但接口可以用于声明引用类型变量。当使用接口来声明引用类型变量时,这个引用类型变量必须引用到其实现类的对象。除此之外,接口的主要用途就是被实现类进行实现。
一个类可以实现多个接口,继承使用extends关键字,而实现则使用implements关键字。
单继承多实现
示例代码:
public interface InterA {
void a();
}
public class InterAImpl implements InterA
{
@Override
public void a(){
System.out.println("将接口InterA中定义的抽象方法进行实现!");
}
}
实现接口与继承类相似,一样可以获得所实现接口里定义的常量field、抽象方法、内部类和枚举类定义。让类实现接口需要在类定义后面增加implements部分,当需要实现多个接口时,多个接口之间以英文逗号(,)隔开。一个类可以继承一个父类并同时实现多个接口,implements部分必须放在extends部分之后。
示例代码:
public interface DBobjectType {
public static final int ROOT =0;
public static final int DATABASE =1;
int TABLE=2;
int COLUMN=3;
int INDEX=4;
}
public interface DataConnection{
public abstract void getConnection();
void close();
}
public class ConnectionImpl implements
DBobjectType,DataConnection {
@Override
public void getConnection() {
System.out.println("获取一个连接对象!");
}
@Override
public void close() {
System.out.println("将连接进行关闭!");
}
public static void main(String[]args){
ConnectionImpl impl=new ConnectionImpl();
System.out.println("数据对象类型:"+ConnectionImpl.ROOT);
impl.getConnection();
}
}
一个类实现了一个或多个接口之后,这个类必须完全实现这些接口里所定义的全部抽象方法,否则该类将保留从父接口那里继承到的抽象方法,该类也必须定义成抽象类。
15.7接口与抽象类的差异
- 接口里只能包含抽象方法,不包含已经提供实现的方法,抽象类则完全可以包含普通方法。
- 接口和抽象类里都可以定义静态方法。
- 接口里只能定义静态常量Field,不能定义普通的Field,抽象类里则都可以。
- 接口里不包含构造器,抽象类里可以包含构造器,抽象类里的构造器并不是用来创建对象,而是让其子类调用这些构造器完成属于抽象类的初始化操作。
- 接口里不能包含初始化块,但抽象类则完全可以包含初始化块。
- 一个类最多只有一个父类,包括抽象类,但是一个类可以实现多个接口。
在定义类的时候,我们一般把类定义成一个独立的程序单元。但是在某些情况下,我们会把一个类放在另一个类的内部定义,这个定义在其他类内部的类就被称为内部类,也可以称为嵌套类。包含内部类的类也被称为外部类,也可以称为宿主类。Java从JDK1.1开始引入内部类,内部类的主要作用如下:
- 内部类提供了更好的封装,可以把内部类隐藏在外部类之内,不允许同一个包中其他类访问该类。
- 内部类成员可以直接访问外部类的私有数据,因为内部类被当成外部类的成员,同一个类成员之间可以相互访问。
- 匿名内部类适合用于创建那些仅需要一次使用的类。
16.1内部类分类
16.2成员内部类
16.2.1定义格式
修饰符 class 类名称{
修饰符 class 类名称{}
}
注:
- 内部类使用外部类,可以随意访问;
- 外部类使用内部类,需要内部类对象
16.2.2成员内部类使用
- 间接方式:在外部类的方法中使用内部类,然后main只是调用外部类的方法
public class Body {
public class Heart{
public void beat(){
System.out.println("心脏跳动:蹦蹦蹦~");
System.out.println("我叫:"+name);
}
}
private String name;
public void methodBody(){
System.out.println("外部类的方法");
new Heart().beat();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class Test{
public static void main(String[] args) {
Body body=new Body();
body.methodBody();
}
}
语法: 外部类.内部类 内部类对象 = new 外部类().new 内部类(); 举例: Outer.Inner in = new Outer().new Inner();
public class Body {
public class Heart{
public void beat(){
System.out.println("心脏跳动:蹦蹦蹦~");
System.out.println("我叫:"+name);
}
}
private String name;
public void methodBody(){
System.out.println("外部类的方法");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class Test{
public static void main(String[] args) {
Body.Heart heart=new Body().new Heart();
heart.beat();
}
}
16.2.3内部类访问同名变量
如果出现了重名现象,访问外部类变量的格式是:
外部类名称.this.外部类成员变量名
public class Outer {
int num=10;
public class Inner{
int num=20;
public void methonInner (){
int num=30;
System.out.println(num);
System.out.println(this.num);
System.out.println(Outer.this.num);
}
}
}
16.3局部内部类
一个类是定义在一个方法内部的,那么这就是一个局部内部类
只有当前所属的方法才能使用它,出了这个方法外面就不能用了
定义格式
修饰符 class 外部类名称{
修饰符 返回值类型 外部类方法名称(参数列表){
class 局部内部类名称{}
}
}
局部内部类:就像普通对象一样直接创建:Inner in = new Inner();
步骤
- 创建局部内部类对象,创建位置:局部内部类所在的方法内部,局部内部类的外部;
- 然后再由外部类去创建外部类对象去调用内部类所在的方法
public class Outer {
public void methodOuter(){
class Inner{
int num=10;
public void methodInner(){
System.out.println(num);
}
}
Inner inner=new Inner();
inner.methodInner();
}
}
class Test{
public static void main(String[] args) {
Outer outer=new Outer();
outer.methodOuter();
}
}
局部内部类final问题
局部内部类:如果希望访问所在方法的局部变量,那么这个局部变量必须是有效final的
从java8+开始,只要局部变量事实不变,那么final关键字可以省略.
public class Outer {
public void methodOuter(){
int num=10;
class Inner{
public void methodInner(){
System.out.println(num);
}
}
Inner inner=new Inner();
inner.methodInner();
}
}
16.4类的权限修饰符
定义一个类的时候,权限修饰符规则:
- 外部类:public/(default)
- 成员内部类:public/protected/(default)/private
- 局部内部类:什么都不能写
16.5匿名内部类(内部类中最重的知识)
如果接口的实现类(或者父类的子类)只需要使用唯一一次,那么这种情况下就可以省略掉该类的定义,而改为使用匿名内部类
左接口右实现类&左父右子都是多态
匿名内部类的定义格式
接口名称 对象名 =new 接口名称(){
};
上述格式是直接写在测试类里面的,而 new 接口名称(){} 是一个没有名字(匿名类)的实现类=>内部类==>匿名内部类
代码示例
使用匿名内部类创建对象
public interface DemoInter {
public abstract void say();
}
class Test {
public static void main(String[] args) {
DemoInter demoInter=new DemoInter() {
@Override
public void say() {
System.out.println("说话~");
}
};
}
}
使用匿名内部类创建匿名对象
public interface DemoInter {
public abstract void say();
}
class Test {
public static void main(String[] args) {
new DemoInter() {
@Override
public void say() {
System.out.println("说话~");
}
}.say();
}
}
注意事项
- 匿名内部类在创建对象的时候只能使用一次(下一次就需要重新new了,就相当于又创建了一个)
- 匿名对象在调用方法的时候,只能调用唯一一次
- 匿名内部类省略了实现类/子类名称,匿名对象省略了对象名称
16.5静态内部类
使用static修饰符来修饰内部类就称为静态内部类,则这个内部类就属于外部类本身,而不属于外部类的某个对象。因此使用static修饰的内部类被称为静态内部类。
静态内部类可以包含静态成员,也可以包含非静态成员。根据静态成员不能访问非静态成员的规则,静态内部类不能访问外部类的实例成员,只能访问外部类的类成员。即使是静态内部类的实例方法也不能访问外部类的实例成员,只能访问外部类的静态成员。
public class Factory {
public static String noodle="面条";
public static String dumplings="水饺";
private String chicken="鸡肉";
public void madamFood(String food) {
System.out.println("厂长吃的是自己夫人做的饭!"+food);
}
public static void diningFood(String food) {
System.out.println("工人吃的是食堂做的饭!"+food);
}
static class DiningRoom {
public static void eat(){
diningFood(Factory.noodle);
}
public void managerEat() {
madamFood("abc");
}
}
}
分析:在上述示例中,静态内部类DiningRoom在进行编译时报错,提示对象的实例上调用了不在范围内的数据信息。因为静态内部类会跟随外部类的静态的信息同时存在,此时可以创建静态内部类的实例对象,但是并不一定会创建外部类的实例对象,那么去访问外部类实例对象的成员就会出问题,因为对象都没有,怎么访问对象上的成员呢?
语法: 外部类.内部类 内部类对象 = new 外部类.内部类(); 举例: Outer.Inner in = new Outer.Inner();
Lambda省去面向对象的条条框框,格式由三部分组成:
Lambda表达式的标准格式为:
(参数类型 参数名称)->{代码语句}
格式说明:
- 小括号内的语法与传统方法参数列表一致:无参数则留空,多个参数则用逗号分隔
-> 是新引入的语法格式,代表指向动作- 大括号内的语法与传统方法体要求基本一致
Lambda表达式无参数无返回值
代码示例:
public interface Cook{
public abstract void makefood();
}
class Test {
public static void main(String[] args) {
invokeCook(new Cook() {
@Override
public void makefood() {
System.out.println("吃饭啦!~);
}
});
invokeCook(()->{
System.out.println("吃饭啦~~!");
});
}
public static void invokeCook( Cook cook){
cook.makefood();
}
}
Lambda表达式有参数有返回值
代码示例1:
class People {
private String name;
private int age;
public People() {
}
public People(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 "Test{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
class Demo01Arrays{
public static void main(String[] args) {
People []arr={
new People("柳岩", 13),
new People("黑客", 18),
new People("囍", 32)
};
Arrays.sort(arr, new Comparator<People>() {
@Override
public int compare(People o1, People o2) {
return o1.getAge()-o2.getAge();
}
});
Arrays.sort(arr, (People o1, People o2)->{
return o1.getAge()-o2.getAge();
});
for (People people : arr) {
System.out.println(people);
}
}
}
代码示例2:
public interface Calculator {
public abstract int calc(int a,int b);
}
class Demo01{
public static void main(String[] args) {
invokeCalc(23, 12, (int a,int b)->{
return a+b;
});
}
public static void invokeCalc(int a,int b,Calculator c){
int sum = c.calc(a, b);
System.out.println(sum);
}
}
Lambda省略格式
Lambda表达式:可推导,可省略
凡是根据上下文推导出来的,都可以省略书写
可以省略的内容
- (参数列表):括号中参数列表的数据类型,可以省略不写
- (参数列表):括号中的参数如果只有一个,那么类型和()都可以省略
- (一些代码):如果{}中的代码只有一行,无论是否有返回值,都可以省略{},return,分号
类做成员变量
-
做成员变量的时候直接new… public class Hero{
private String name;
private Weapon weapon=new Weapon();
}
-
private私有化,通过getter,setter为其赋值 public class Hero{
private String name;
private int age;
private Weapon weapon;
}
class Weapon{
private String code;
public void Gun(){
System.out.println("砰砰砰~~~");
}
}
class Tes{
public static void main(String[] args) {
Hero hero=new Hero();
hero.setAge(12);
hero.setName("gy");
hero.setWeapon(new Weapon());
hero.getWeapon().Gun();
}
}
-
通过构造方法为其赋值
public class Hero{
private String name;
private int age;
private Weapon weapon;
}
class Weapon{
private String code;
public void Gun(){
System.out.println("砰砰砰~~~");
}
}
class Tes{
public static void main(String[] args) {
Hero hero=new Hero("bsjka", 12, new Weapon());
hero.getWeapon().Gun();
}
}
接口做成员变量
private私有化,通过getter,setter为其赋值
- 用接口去创建变量来作为成员变量,new接口实现类来作为其值(多态:左接口右实现类)
public class Hero {
private String name;
private skill skill;
}
interface skill{
void use();
}
class SkillImpl implements skill{
@Override
public void use() {
System.out.println("滴滴滴滴~");
}
}
class Tes{
public static void main(String[] args) {
Hero hero=new Hero();
hero.setSkill(new SkillImpl());
hero.getSkill().use();
}
}
- 用接口去创建变量来作为成员变量,new匿名内部类来作为其值(多态:左接口右实现类)
public class Hero {
private String name;
private skill skill;
}
interface skill{
void use();
}
class Tes{
public static void main(String[] args) {
Hero hero=new Hero();
hero.setSkill(new skill() {
@Override
public void use() {
System.out.println("滴滴滴滴~");
}
});
hero.getSkill().use();
}
}
通过构造方法为其赋值
做成员变量的时候直接new…
接口做方法的参数或方法返回值类型
public class Hero {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
List<String> result= addNames(list);
for (int i = 0; i < result.size(); i++) {
System.out.println(result.get(i));
}
}
public static List<String> addNames(List<String> list){
list.add("acs");
list.add("sv");
list.add("sfa");
list.add("etgfv");
list.add("hedd");
return list;
}
}
acs
sv
sfa
etgfv
hedd
Process finished with exit code 0
在模板模式中,抽象类公开了定义的方法/模板来执行它的方法。 它的子类可以根据需要重写方法实现,但调用的方式与抽象类定义的方式相同。 此模式属于行为模式类别。
步骤
- 创建一个抽象类,里面有一个模板方法(抽象方法)
- 模板方法是不允许修改的,所以最好用final修饰,保证其不会被子类修改
- 如果模板方法里面还有方法,那么这个方法也要是抽象的
- 继承抽象类的子类覆盖重写模板方法
- 在测试类里面创建子类对象,调用模板方法
示例
示例代码1:
public abstract class Game {
abstract void initialize();
abstract void startPlay();
abstract void endPlay();
public final void play(){
initialize();
startPlay();
endPlay();
}
}
public class Cricket extends Game {
@Override
void endPlay() {
System.out.println("Cricket Game Finished!");
}
@Override
void initialize() {
System.out.println("Cricket Game Initialized! Start playing.");
}
@Override
void startPlay() {
System.out.println("Cricket Game Started. Enjoy the game!");
}
}
public class Football extends Game {
@Override
void endPlay() {
System.out.println("Football Game Finished!");
}
@Override
void initialize() {
System.out.println("Football Game Initialized! Start playing.");
}
@Override
void startPlay() {
System.out.println("Football Game Started. Enjoy the game!");
}
}
public class TemplatePatternDemo {
public static void main(String[] args) {
Game game = new Cricket();
game.play();
System.out.println();
game = new Football();
game.play();
}
}
Cricket Game Initialized! Start playing.
Cricket Game Started. Enjoy the game!
Cricket Game Finished!
Football Game Initialized! Start playing.
Football Game Started. Enjoy the game!
Football Game Finished!
示例代码2:
package designpattern.templatemethod;
public abstract class BankTemplateMethod {
public void takeNumber(){
System.out.println("取号排队");
}
public abstract void transact();
public void evaluate(){
System.out.println("反馈评分");
}
public final void process(){
this.takeNumber();
this.transact();
this.evaluate();
}
}
package designpattern.templatemethod;
public class Client {
public static void main(String[] args) {
BankTemplateMethod btm = new DrawMoney();
btm.process();
BankTemplateMethod btm2 = new BankTemplateMethod() {
@Override
public void transact() {
System.out.println("我要存钱!");
}
};
btm2.process();
BankTemplateMethod btm3 = new BankTemplateMethod() {
@Override
public void transact() {
System.out.println("我要理财!我这里有2000万韩币");
}
};
btm3.process();
}
}
class DrawMoney extends BankTemplateMethod {
@Override
public void transact() {
System.out.println("我要取款!!!");
}
}
示例代码3:
public abstract class Hero {
public void UseTime (){
long t1 = System.currentTimeMillis();
gy();
long t2 = System.currentTimeMillis();
System.out.println("耗时:"+(t2-t1)+"ms");
}
protected abstract void gy();
}
class Some extends Hero{
@Override
protected void gy() {
String str="syt";
for (int i = 0; i < 521; i++) {
str+=i;
}
System.out.println(str.length());
}
public static void main(String[] args) {
Some some=new Some();
some.UseTime();
}
}
注:模板方法是code();把模板方法写在了一个方法A里面,子类覆盖重写模板方法之后,对象去调A来运行模板方法
抽象工厂模式是一个超级工厂,用来创建其他工厂。 这个工厂也被称为工厂的工厂。 这种类型的设计模式属于创建模式,因为此模式提供了创建对象的最佳方法之一。
在抽象工厂模式中,接口负责创建相关对象的工厂,而不明确指定它们的类。 每个生成的工厂可以按照工厂模式提供对象。
工厂方法模式分为三种:
- 普通工厂模式
就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建。首先看下关系图:
举例如下:(我们举一个发送邮件和短信的例子)
public interface Sender {
public void Send();
}
public class MailSender implements Sender {
@Override
public void Send() {
System.out.println("this is mailsender!");
}
}
public class SmsSender implements Sender {
@Override
public void Send() {
System.out.println("this is sms sender!");
}
}
public class SendFactory {
public Sender produce(String type) {
if ("mail".equals(type)) {
return new MailSender();
} else if ("sms".equals(type)) {
return new SmsSender();
} else {
System.out.println("请输入正确的类型!");
return null;
}
}
}
public class FactoryTest {
public static void main(String[] args) {
SendFactory factory = new SendFactory();
Sender sender = factory.produce("sms");
sender.Send();
}
}
- 多个工厂方法模式
是对普通工厂方法模式的改进,在普通工厂方法模式中,如果传递的字符串出错,则不能正确创建对象,而多个工厂方法模式是提供多个工厂方法,分别创建对象。关系图:
将上面的代码做下修改,改动下SendFactory类就行,如下:
public interface Sender {
public void Send();
}
public class MailSender implements Sender {
@Override
public void Send() {
System.out.println("this is mailsender!");
}
}
public class SmsSender implements Sender {
@Override
public void Send() {
System.out.println("this is sms sender!");
}
}
public class SendFactory {
public Sender produceMail(){
return new MailSender();
}
public Sender produceSms(){
return new SmsSender();
}
}
public class FactoryTest {
public static void main(String[] args) {
SendFactory factory = new SendFactory();
Sender sender = factory.produceMail();
sender.Send();
}
}
将上面的多个工厂方法模式里的方法置为静态的,不需要创建实例,直接调用即可。
public interface Sender {
public void Send();
}
public class MailSender implements Sender {
@Override
public void Send() {
System.out.println("this is mailsender!");
}
}
public class SmsSender implements Sender {
@Override
public void Send() {
System.out.println("this is sms sender!");
}
}
public class SendFactory {
public static Sender produceMail(){
return new MailSender();
}
public static Sender produceSms(){
return new SmsSender();
}
}
public class FactoryTest {
public static void main(String[] args) {
Sender sender = SendFactory.produceMail();
sender.Send();
}
}
总体来说,工厂模式适合:凡是出现了大量的产品需要创建,并且具有共同的接口时,可以通过工厂方法模式进行创建。在以上的三种模式中,第一种如果传入的字符串有误,不能正确创建对象,第三种相对于第二种,不需要实例化工厂类,所以,大多数情况下,我们会选用第三种——静态工厂方法模式
什么是策略模式
- 比如说对象的某个行为,在不同场景中有不同的实现方式,这样就可以将这些实现方式定义成一组策略,每个实现类对应一个策略,在不同的场景就使用不同的实现类,并且可以自由切换策略。
- 在策略模式中,可以在运行时更改类行为或其算法。 这种类型的设计模式属于行为模式。
- 在策略模式中,创建表示各种策略对象和其行为根据其策略对象而变化的上下文对象。 策略对象更改上下文对象的执行算法。
策略模式的优点:
1、干掉繁琐的 if、switch 判断逻辑;
2、代码优雅、可复用、可读性好;
3、符合开闭原则,扩展性好、便于维护;
策略模式步骤
- 定义一个接口(也可以是抽象类),写抽象方法
- 写几个实现类,去实现接口中的抽象方法
- 创建上下文(
Context )类(也可以和测试类合二为一),里面用接口做成员变量,通过构造方法传实现类的值(new…),左接口右实现类=>多态,传入不同的实现类就会有不同的策略
示例代码1:
public interface Strategy {
public int doOperation(int num1, int num2);
}
public class OperationAdd implements Strategy{
@Override
public int doOperation(int num1, int num2) {
return num1 + num2;
}
}
public class OperationSubstract implements Strategy{
@Override
public int doOperation(int num1, int num2) {
return num1 - num2;
}
}
public class OperationMultiply implements Strategy{
@Override
public int doOperation(int num1, int num2) {
return num1 * num2;
}
}
public class Context {
private Strategy strategy;
public Context(Strategy strategy){
this.strategy = strategy;
}
public int executeStrategy(int num1, int num2){
return strategy.doOperation(num1, num2);
}
}
public class StrategyPatternDemo {
public static void main(String[] args) {
Context context = new Context(new OperationAdd());
System.out.println("10 + 5 = " + context.executeStrategy(10, 5));
context = new Context(new OperationSubstract());
System.out.println("10 - 5 = " + context.executeStrategy(10, 5));
context = new Context(new OperationMultiply());
System.out.println("10 * 5 = " + context.executeStrategy(10, 5));
}
}
10 + 5 = 15
10 - 5 = 5
10 * 5 = 50
示例代码2:
public class Test3 {
public static void main(String[] args) {
Strategy strategy = new ConcreteStrategy1();
Context context = new Context(strategy);
context.doAnythinig();
}
}
interface Strategy {
public void doSomething();
}
class ConcreteStrategy1 implements Strategy {
public void doSomething() {
System.out.println("具体策略1的运算法则");
}
}
class ConcreteStrategy2 implements Strategy {
public void doSomething() {
System.out.println("具体策略2的运算法则");
}
}
class Context {
private Strategy strategy;
public Context(Strategy _strategy){
this.strategy = _strategy;
}
public void doAnythinig(){
this.strategy.doSomething();
}
}
22.1简介
? Java是面向对象编程语言,但同样提供了8中基本数据类型,这8种基本数据类型不支持面向对象的编程机制,基本数据类型同样也不具备“对象”的特征:没有Field成员变量、没有方法可以被调用。 8种基本数据类型带来一定的方便性,即:可以进行简单、有效的常规数据处理。但在某些时候基本数据类型会有一些制约。例如:所有的引用类型的数据都继承Object类,都可以当成Object类型变量使用,但是基本数据类型则不可以,如果某些类提供的方法需要Object类型的参数,但实际的数值确是1、2、3、4等数值,这种情况就难以处理。 为了解决8种基本数据类型的变量不能当成Object类型变量使用的问题,Java提供了包装类的概念,为8种基本数据类型分别定义了相应的引用类型,并称为基本数据类型的包装类。
基本数据类型 | byte | char | short | int | long | float | double | boolean |
---|
包装类 | Byte | Character | Short | Integer | Long | Float | Double | Boolean |
22.2包装类的初始化
Java提供的包装类提供了相似的构造方法用于创建包装类对象,基本上是传入对应的基本数据类型或者字符串形式的数据
public static void main (String[]args) {
Integer int1=new Integer(5);
Integer int2 = new Integer("12");
Boolean bo1= new Boolean(true);
Boolean bo2 =new Boolean("false");
Double do1 =new Double(12.5);
Double do2= new Double("30.1");
}
分析:通过上面示例可以看出,所有基本数据类型包装类,都提供通过向包装类构造器中传入一个字符串参数的方式或者对应的基本数据类型的方式来创建包装类对象。但是如果传入的字符串参数不符合基本数据类型格式,那么将引发java.lang.NumberFormatException异常。数据转换格式化异常
public static void main (String[]args){
Integer int1 = new Integer("abc");
System.out.println(int1);
}
22.3装箱与拆箱
Java提供的基本数据类型与包装类之间的转换有点繁琐,在JDK1.5版本中提供了自动装箱与拆箱的功能。所谓的自动装箱与拆箱就是可以把一个基本类型的数据变量赋值为包装类变量,或者赋值给Object变量,子类对象可以直接赋值给父类变量就是装箱的一种体现。 自动拆箱则与之相反,允许直接把包装类对象直接赋值给一个对应的基本类型变量。
public static void main(String[]args) {
Integer int1=12;
System.out.println(int1);
int num =new Integer(5);
System.out.println(num);
}
22.4基本数据类型与字符的转换
字符转基本类型
在某些情况下,数值的表示形式不是由基本数据类型来表示的,而是由字符串类型的数值的形式来表示的,如果这种类型要进行算术运算时就需要提前将该字符串类型的数字转换成对应的基本数据类型。该如何来实现呢?
Java提供的包装类就可以帮助大家来解决这个问题。
1、利用包装类提供的parseXXX(Strings)的静态方法,将String类型的数据转换成相应的基本数据类型(XXX)。
2、利用包装类提供的Xxx(Strings)构造器。
3、String类型提供了多个valueOf()方法,用于将基本类型变量转换成字符串。
public static void main(String[]args){
String str1="23";
String str2="7";
int num1 = Integer.parseInt(str1);
int num2= Integer.parseInt(str2);
System.out.println("两个字符串形式的数值相加的和是:"+(num1+num2));
doublenum=12.5;
String numStr = String.valueOf(num);
System.out.println(numStr);
}
? 分析:通过以上程序不难看出,每一个基本数据类型所对应的包装类,都有一个parseXXX的方法,来将一个字符串类型的数值,转换成其对应的基本数据类型。Long.parseLong,Double.parseDoule等,但是需要注意的是这个字符串类型的数值中不能包含非数字的字符,否则会抛出java.lang.NumberFormatException异常。
? 将一个基本数据类型转换成字符串,除了使用String.valueOf的方法之外,最简洁的操作就是使用“+”的拼接功能,让一个基本数据类型与一个空“”字符串相加,结果即可得到该数值类型对应的字符串。但是建议大家在开发时还是通过方法调用的方式来实现。
基本类型转字符
- 基本数据类型.toString();
这个toString()是基本数据类型的静态方法
String s=int.toString(20);
- 将基本数据类型封装成相应的封装类型对象,采用"基本数据类型对象.toString()"的方法
Double d =new Double(321.2);
String s=d.toString();
-
基本数据类型+""<=>""+基本数据类型<=>字符串类型 String s=""+200;
String s=200+"";
-
String类的静态方法ValueOf()
String.valueOf('b');
枚举类型是Java5中新增特性的一部分,它是一种特殊的数据类型,之所以特殊是因为它既是一种类(class)类型却又比class类型多了些特殊的约束,但是这些约束的存在也造就了枚举类型的简洁性、安全性以及便捷性。枚举类和类定义不同,定义枚举类时不再使用class关键字,而是使用enum关键字
枚举的对象是有限个,需要使用常量的形式,枚举在首行
获取对象,直接"枚举名.枚举值/对象名"就可以啦再由对象去调成员变量和成员方法
public enum Season {
SPRING,SUMMER,AUTUMN,WINTER;
private String nickName;
private String feature;
public void show(){
System.out.println("我是一个对象~");
}
Season(String nickName, String feature) {
this.nickName = nickName;
this.feature = feature;
}
Season() {
}
}
- 枚举类的构造方法只能是private修饰,因为枚举类的对象是定义类时就已经定义好的,是不允许随意创建该类的实例对象,所以只能是private修饰。
- 在定义枚举类时,定义的枚举值成员实际上就是调用的构造方法,只不过是默认的构造方法,所以可以不用显示调用,也不要写构造方法名,一旦定义了构造方法只能显示调用,就必须显示调用但仍不需要写构造方法名。
成员变量可以直接写在枚举值/对象后面
做法:先在对象后面写一个"()",然后往里面放成员变量值就好了
public enum Season {
SPRING("春光","无限"),SUMMER("夏天","热"),AUTUMN("秋","凄凉"),WINTER("冬天","冷");
private String nickName;
private String feature;
public void show(){
System.out.println("我是一个对象~");
}
Season(String nickName, String feature) {
this.nickName = nickName;
this.feature = feature;
}
Season() {
}
}
枚举值/对象可以用匿名内部类重写方法,来获得有自己特征的方法
做法:在对象后面先写"(){}",然后在"{}"里面Generate…=>OverrideMethods…
public enum Season {
SPRING,SUMMER,AUTUMN,WINTER(){
@Override
public void show() {
System.out.println("肝胆皆冰雪~");
}
};
private String nickName;
private String feature;
public void show(){
System.out.println("我是一个对象~");
}
Season(String nickName, String feature) {
this.nickName = nickName;
this.feature = feature;
}
Season() {
}
}
枚举类提供的可操作的方法为:
?name():返回枚举值的变量名
?values():枚举类提供的静态方法,返回由所有的枚举对象组成的数组。
public class Test {
public static void main (String[] args) {
for(Day day:Day.values()) {
System.out.println(day.name());
}
}
}
|