自用Java笔记(Ⅲ),主要记录Java面向对象!奋斗ing
面向对象
Java类及类的成员:属性、方法、构造器;代码块、内部类
类与对象
类的成员
- 属性:Field = 域、字段 = 成员变量
- 行为:Method =(成员)方法 = 函数
对象的内存解析
堆(Heap)存放对象实例
栈(Stack)存储局部变量
方法区(Method Area)存储已被虚拟机加载的类信息、变量、静态变量、即时编译器编译后的代码等
理解万事万物皆对象
- 在Java语言范畴中,我们将功能,结构等封装到类中,通过类的实例化,来调用具体的功能结构。
涉及到Java与前端Html、后端的数据库交互时,前后端的结构在Java层面交互时,都体现为类、对象。
PS:引用类型的变量,只可能储存两类值,null 或 地址值(含变量的类型)
匿名对象的使用,只能调用一次
属性
属性(成员变量)VS 局部变量
相同点
定义变量格式相同,先声明后使用,且都有对应的作用域
不同点
声明的位置不同
属性:直接定义在类的{}中
局部变量:声明在方法内、方法形参、代码块内、构造器形参、构造器内部的变量
权限修饰符的不同
属性:可以在声明属性时,使用权限修饰符指明其权限
局部变量:不可以使用
默认初始化值的情况
属性:类的属性根据类型都有默认初始化只
局部变量:无初始化值
在内存中加载的位置
属性:堆空间(非static)
局部变量:栈空间
方法
方法重载(overload)
定义:在同一个类中允许存在一个以上的同名方法,只要他们的参数个数或者类型不同即可。
“两同一不同”:同一个类,同一个方法名。参数列表不同。
可变个数的形参
格式:数据类型 … 变量名, 必须申明在末尾,可传入参数个数为0个及以上
方法参数的值传递机制
变量赋值:
- 基本数据类型:赋值的是变量所保存的数据值
- 引用数据类型:赋值变量所保存数据的地址值
面向对象的三个特征:封装性、继承性、多态性、(抽象性)
封装与隐藏
体现:
- 将类的属性XXX私有化private,提供公有化public方法来获取getXXX和设置属性setXXX的值。
- 不对外暴露的私有的方法
- 单例模式(将构造器私有化)
- 如果不希望类在包外被使用可以设置成缺省
目标:高内聚,低耦合
PS:封装性的体现需要权限修饰符来体现。修饰类只能public与缺省
权限修饰符(从小到大)
修饰符 | 类内部 | 同一个包 | 不同包的子类 | 同一个工程 |
---|---|---|---|---|
private | √ | |||
(缺省) | √ | √ | ||
protect | √ | √ | √ | |
public | √ | √ | √ | √ |
继承性inheritance
好处:
- 减少了代码的多余,提高代码的复用性
- 便于功能的扩展
- 为之后多态性的使用,提供了前提
格式:
1 | class A extends B{} |
- A:子类、派生类、subclass
- B:父类、超类、superclass
ps:Java只支持单继承和多继承,不允许多重继承,一个子类只能有一个父类。如果没有显式的声明一个类的父类的话,则此类继承于java.lang.Object类。
方法的重写(override/overwrite)
定义:在子类中根据需要对父类中的方法进行改造。
应用:重写以后,当创建子类对象,调用同名方法时调用的是重写的方法。
规定:
子类重写的方法名与形参列表与被重写一样
子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符
ps:特殊情况,子类中不能重写父类中声明为private的方法。
返回类型:
- 父类被重写方法的返回类型为void,子类重写的方法只能返回void;
- 父类被重写方法的返回类型为基本类型,子类重写的方法只能返回相同的基本类型;
- 父类被重写方法的返回类型为A类型,子类重写的方法可以返回A类型或A的子类;
异常类型:子类重写的方法的异常类型不大于父类被重写的方法抛出的异常类型
特别注意:子类和父类的同名同参数的方法要么声明非static(考虑重写),要么都声明为static(不是重写)
类的成员之三:构造器(构造方法constructor)
作用:创建对象;给对象进行初始化
格式:权限修饰符 + 类名(形参列表){}
ps:构造器可重载,且一旦显示定义了类的构造器之后,系统不再提供默认空参构造器。
多态性polymorphism
定义:一个事物的多种形态,在Java中的体现:对象的多态性,父类的引用指向子类的对象
多态的使用:
在编译期,只能调用父类中声明的方法,在运行期,实际执行的是子类中的重写的方法。
虚拟方法调用(Virtual Method Invocation)
Java引用变量有两种类型:编译时类型和运行时类型。
调用方法时,编译看左边,执行看右边。–动态绑定,多态是运行时行为。重载是编译是就已经确定了,“早绑定”,”静态绑定”。
Bruce Eckel:”不要犯傻,如果它不是晚绑定,就不是多态。”
多态性的使用前提:
- 类的继承关系
- 方法的重写
ps: 对象的多态性,只适用于方法,不适用于属性(编译和运行都看左边)
向下转型
编译时只能调用父类声明的属性和方法,如何调用子类特有的属性和方法? 使用强制类型转换。使用强制转换时,可能出现ClassCastException的异常。
使用instanceof进行检测:
1 | a instanceof A //判断duixa是否是A的实例,如是返回true。 |
其他关键字:this、super、static、final、abstract、interface、package、import与相关补充知识
重要关键字
this:
理解为 当前对象,通常可以省略。可以调用:属性、方法和构造器。
调用本类中指定的构造器 this(形参列表)
ps: import static导入指定类或接口中的静态结构:属性、方法
super:
理解为 父类的,可以调用:属性、方法和构造器。
通常可以省略,当子类和父类中定义同名的属性时,需要显式的super
特殊情况, 当子类重写父类的方法时,需要调用父类中的方法时,需要显式的super
super调用父类构造器:在子类构造器中显式的“super(参数列表)”,且必须声明在子类构造器中的首行
在类的构造器中,针对“super(参数列表)”和“this(参数列表)”只能二选一,构造器中没有,默认super(空参)
在类的多个构造器中,至少有一个类的构造器使用了super(参数列表),调用父类的构造器。
static:
某些特定的数据在内存中只有一份。eg:每个中国人都共享中国这个国籍。
修饰:属性、方法、代码块、内部类
修饰属性:静态变量
- 属性按是否有static修饰又分为:静态属性 VS 非静态属性(实例变量)
- 实例变量:当创建类的多个对象,每个对象都独立拥有一套类中的非静态属性,当修改某一个对象的静态属性时,不会导致其他对象中同样的属性值的修改。
- 静态属性:创建类的多个对象,每个对象都共享同一个静态属性。当修改某一个对象的静态属性时,会导致其他对象调用此静态变量时,属性值是修改的。
- PS:静态变量随类的加载而加载,且早于对象的加载。因此可以通过”类.静态变量“进行调用。由于类只会加载一次 ,则静态变量在内存中也只会存在一份。
修饰方法:静态方法
- 可以通过”类.静态方法“进行调用
- 静态方法只能调用静态的方法或属性,非静态则都可以。
- 在静态方法中,不能使用this,super关键字
static应用场景:
- 属性是可以被多个对象共享,不会随着对象的不同而改变
- 操作静态属性的方法,设置成static;工具类中的方法,习惯上声明为static
final:
可以修饰的结构:类、方法、变量
- 修饰类:此类不能被其他类继承。eg:String类、System类、StringBuffer类。
- 修饰方法:此方法不能被重写。eg:Object类中的getClass()。
- 修饰变量:此“变量”被称为一个常量。
- 修饰属性,可以考虑的赋值位置有显式初始化、代码块中初始化、构造器中初始化、不可以在方法中赋值。
- 修饰局部变量:尤其修饰形参时,表明此形参是常量。
- static final 修饰属性:全局常量
abstract:抽象类与抽象方法
修饰的结构:类、方法
- 修饰类:抽象类,不可实例化,类中一定要构造器,便于子类实例化时调用(涉及:子类对象实例化的全过程)开发中,都会提供抽象类的子类。让子类对象实例化,完成相关的。
- 修饰方法:抽象方法只有方法的声明,没有方法体。包含抽象方法的类一定是一个抽象类,抽象类中可以没有抽象方法。若子类重写了父类的所有的抽象方法后,此子类方可实例化。若未重写,则该子类也是一个抽象类,需要abstract修饰。
- PS:abstract不可以用来修饰属性、构造器等。abstract不能修饰私有方法、静态方法、final的类与方法。
抽象类的匿名子类
1 | Worker worker = new Worker(); |
接口(interface)
与类并列的结构,一定程度解决类的单继承性的缺陷。本质是契约、规范、标准。(JDK7及以前)接口是一种特殊的抽象类,这种抽象类只包含常量和方法的定义。
继承是一个“是不是”的关系,而接口实现的是“能不能”的关系。
接口中的成员:
- JDK1.7及以前,只能定义全局变量和抽象方法
- 全局变量:public static final的
- 抽象方法:public abstract的
- JDK8,出来定义以上之外,还可以定义静态方法、默认方法。
- JDK1.7及以前,只能定义全局变量和抽象方法
接口中不能定义构造器!意味着接口不能实例化
Java开发中,接口通过让类去实现(implement)的方法来使用(面向接口编程),若实现类覆盖了接口的所有的抽象方法后,此实现类方可实例化。若未重写,则该实现类也是一个抽象类。
Java类可以实现多个接口,弥补类的单继承性的局限性。
1
2//先写extends再写implements
class AA extends BB implements CC,DD,EE接口与接口之间可以继承,而且可以多继承。
接口的具体使用,体现了多态性、
接口的应用:
代理模式(Proxy)
为其他对象提供一种代理以控制对这个对象的访问,另一个博文(中介)应用场景:
安全代理
远程代理
延迟加载
分类:
- 静态代理(静态定义代理类)
- 动态代理(动态生存代理类)
- 工厂模式
Java8中关于接口的改进
接口中定义的静态方法,只能通过接口来调用(像工具类)
通过实现类的对象,可以调用接口中的默认方法。
类优先原则:若子类(或实现类)继承的父类和实现的接口中声明了同名同参数的方法,那么子类在没有重写此方法的情况下,默认调用的是父类的同名同参数的方法。、
接口冲突:若实现类实现了多个接口,而多个接口都定义了同名同参数的默认方法,在实现类没有重写的情况下,报错。因此实现类必须重写此方法。
如何在子类(实现类)的方法中调用父类、接口中被重写的方法:
1
2
3method();//调用自己重写的方法
super.method();//调用父类中声明的
Interface1.super.method();//调用接口中的默认方法
补充
类的成员之四:代码块(初始化块)
就是一对大括号,用来初始化类、对象,若有修饰,只能是static
- 静态代码块
- 内部可以有输出语句,并随着类的加载而执行,且只执行一次。
- 若多个静态代码块,按声明顺序依次执行,总优先于非静态代码块
- 静态代码块内只能静态的属性、方法,不能调用非静态的结构
- 作用:初始化类的信息
- 非静态代码块
- 内部可以有输出语句,随着每次对象的创建而执行。
- 非静态代码块内既能静态的属性、方法,也能调用非静态的结构
属性赋值的相关问题:
可以对属性进行赋值的位置:
- 默认初始化
- 显式初始化
- 构造器中初始化
- 有对象后,通过“对象.属性”或“对象.方法”的方法进行赋值
- 在代码块中进行赋值
属性赋值的先后顺序:1 -> 2 / 5 -> 3 -> 4
类的成员之五:内部类
类A声明在类B中,A为内部类
分类:成员内部类 (静态、非静态) VS 局部内部类(方法、代码块、构造器内)
成员内部类:
- 作为外部类的成员:调用外部类的结构、可以被static修饰、可以被四种权限修饰符修饰
- 作为一个类:内可以定义属性、方法、构造器等,可以被final修饰,可以被abstract修饰
- 相关使用细节:
1 | //如何实例化成员内部类的对象: |
JavaBean
符合以下标准的Java类:
- 类是公共的
- 有一个无参的公共的构造器
- 有属性,且有对应的get、set方法
JUnit单元测试方法
- 选中当前工程 - 右键选择:build path - add libraries - JUnit 4 - 下一步
- 创建Java类(要求:①此类是public ②此类提供公共空参构造器)进行单元测试
- 在此类中声明单元测试方法:要求权限为public,没有返回值且没有形参
- PS:需要声明注释@Test,并导入包import org.junit.Test; 左键双击单元测试方法名,右键:run as - JUnit Test
- 若执行结果无异常为绿色,异常为红。
== VS equals()
- == 是运算符,可以用于基本数据与引用类型变量,前者比较保存的数据是否相同(不一定要类型相同,但必须一致),后者比较地址值是否相同,是否引用指向同一个对象
- equals() 是一个方法,只能用于引用数据类型变量的比较,object类中定义的equals() 和 == 的作用是相同的。像String、Date、File、包装类等都重写了Object类中的equals()方法,重写以后比较的是两个对象的实体内容是否相同。若自己定义的类也要有这样的功能,比较对象的实体内容,应该重写equals()方法。
toString()方法
- 输出一个引用变量时,实际上输出的是对象的toString()
- 像String、Date、File、包装类等都重写了Object类中的toString()方法,返回“实体内容”信息
- 自定义类也可以重写该方法。
main()方法
- 作为程序的入口
- 也是一个普通的静态方法(通过实例化类对象调用普通属性与方法)
- 可以作为与控制台交互的方式(java xxxDemo “str”)
包装类(Wrapper)的使用
针对八种基本数据类型定义相应的引用类型-包装类(封装类),有了类的特点,就可以调用类中的方法,实现真正的面向对象。
| 基本数据类型 | 包装类 |
| ———— | ————- |
| byte | Byte |
| short | Short |
| int | Integer |
| long | Long |
| float | Float |
| double | Double |
| boolean | Boolean |
| char | Character |Byte Short Integer Long Float Double 父类为Number
基本数据类型、包装类、String三者的相互转换
包装类 -> 基本数据类型:调用包装类的xxxValue()
基本数据类型 -> 包装类:调用包装类的构造器
基本数据类型、包装类 -> String类型:调用String重载的valueOf(Xxx xxx)
1
2
3
4
5int num1 = 10;
//方式一:连接运算
String str1 = num1 + "";
//方式二:调用String重载的valueOf(Xxx xxx)
String str2 = String.valueOf(num1);String类型 -> 基本数据类型、包装类:调用包装类的parseXxx()
1
2String str3 = "1234";
int num3 = Integer.parseInt(str3);
自动装箱与拆箱(JDK5.0以后)
1
2
3
4int num1 = 10;
Integer in1 = num1; //自动装箱
int num3 = in1;//自动拆箱
1 | Object o1 = true ? new Integer(1) : new Double(2.0); |
设计模式
单例(Singleton)设计模式
只能存在一个对象实例,好处减少了系统性能的开销
实现一:饿汉式
私有化类的构造器
内部创建类的对象(private static)
提供公共的方法(static),返回类的对象
1
2
3
4
5
6
7private Bank(){
}
private static Bank instance - new Bank();
public static Bank getInstance() {
return instance;
}
实现二:懒汉式
私有化类的构造器
声明当前类对象(static),没有初始化
声明public、static的返回当前类对象的方法
1
2
3
4
5
6
7
8
9
10private Order(){
}
private static Order instance - null;
public static Order getInstance() {
if(instance == null) {
instance = new Order();
}
return instance;
}
饿汉式 VS 懒汉式
区别:懒汉式好处延迟对象的创建,饿汉式坏处,对象加载时间太长,但其是线程安全的
使用场景:
- 网站的计算器、
- 应用程序的日志应用
- 数据库连接池
- 读取配置文件的类
- Application也是单例的典型应用
- Windows的Task Manager(任务管理器)
- Windows中的Recycle Bin(回收站)
模板方法设计模式(TemplateMethod)
抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行拓展、改造,但子类总体上会保留抽象类的行为方式。
解决的问题:
- 当功能内部一部分实现是确定的,一部分实现是不确定的。这是可以把不确定部分暴露出来,让子类去实现。
- 在软件开发中实现一个算法时,整体步骤很确定、通用,这些步骤在父类中写好,但部分易变,可以将该部分抽象出来,供不同子类去实现。
MVC设计模式
模型层 model:主要处理数据
- 数据对象封装: model.bean/domain
- 数据库操作类: model.dao
- 数据库: model.db
视图层 view: 显示数据
- 相关工具类: view.utils
- 自定义view: view.ui
控制层 controller: 处理业务逻辑
- 应用界面相关: controller.activity
- 存放fragment: controller.fragment
- 显示列表的适配器: controller.adapter
- 服务相关的: controller.service
- 抽取的基类: controller.base