面向对象
2020-09-30 14:30:02 0 举报
AI智能生成
java面向对象总结
作者其他创作
大纲/内容
三条主线
Java中的类和成员
属性
方法
构造器
代码块
内部类
面向对象的三大特征
封装
继承
多态
(抽象性)
其他关键字
this
super
static
final
abstract
interface
package
import
面向对象编程(上)
一、面向过程与面向对象
面向对象思想
理解类和对象
类:抽象的,概念上的内容
对象:实实在在的个体,在堆空间中分配了空间;对象是由类派生出来的
理解万事万物皆对象:
在java语言范畴中,我们都将功能、结构等封装到类中,通过类的实例化,来调用具体的功能结构
文件:file
网络资源URL
...
涉及到java语言与前端html,后端数据库交互是都体现为类、对象
jvm内存结构
编译完程序之后,生成一个或者多个字节码文件(编译)
使用JVM(Java Virtue Machine)中类的加载器和解释器对生成的字节码文件解释运行;意味着需要将字节码文件对应的类加载到内存中,涉及到内存解析;(解释)
二、Java基本元素:类和对象
Class:类:抽象的描述
类的成员
field:属性 = 成员变量
method:成员函数
Object:对象;实实在在存在的个体
三、对象的创建和使用
面向对象思想落地的实现步骤
1. 创建类
2. 创建类的对象
3. 通过”对象.属性“和”对象.方法“调用对象的结构
花边
如果创建了一个类的多个对象,则每个对象都独立拥有一套类的属性(非static)
赋值和数组一样,把地址赋值给另一个对象,则两个对象指向了堆空间的同一块空间(同一个实体),所以其实完全是同一个东西 #tips
内存解析
介绍
堆(heap):唯一目的是存放对象实例(所有的对象实例和数组都要在堆上分配
对象的属性加载在堆空间中(非static)
栈(stack):主要指虚拟机栈,存储局部变量
局部变量类型
基本数据类型变量
对象引用:reference类型,不等于对象本身,是对象在堆里存放的首地址;方法执行完自动释放
拓展
局部变量:方法内的变量(main方法里的也都是)
成员变量:方法外的变量(类里面的属性)
方法区(Method Area):存储被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等
举例
四、类的成员之一:属性
属性(成员变量) VS 局部变量
相同点
定义变量的格式:数据类型 变量名 = 变量值
先声明,后使用
变量都有其对应的作用域
不同点
1. 声明的位置不同
成员变量:直接在类的{}的变量(属性)
局部变量:(除了成员变量之外的一切变量)声明在方法内、方法形参,代码块内,构造器形参,构造器内部的变量
2. 权限修饰符的不同
成员变量(属性):可以在声明时知名其权限,使用权限修饰符
常用的:private,public,缺省,protected --->封装性
局部变量:不可以使用权限修饰符
3. 默认初始化值的情况
成员变量(属性):根据其类型,都有默认初始化值(跟数组的默认一样)
整型:0
浮点型:0.0
char:0或者'u\\0000'
布尔型:false
引用类型(类,数组,接口):null
局部变量
没有默认初始化值,必须赋值
特别的:形参调用时赋值即可
4. 在内存中加载的位置(非static)(静态的加载到静态域)
成员变量(属性):加载到堆空间
局部变量:加载到栈空间
五:类的成员之二:方法
分类
方法的声明
权限修饰符 返回值类型 方法名(形参列表){ 方法体 }
说明
1. 权限修饰符 -->封装性
private
public
缺省
protected
2. 返回值
有返回值
必须在方法声明时指定返回值的类型,同时方法要使用return关键字指定返回值的类型;
没返回值
方法声明时void,通常不用return,但是如果使用的话,直接”return;“即可,表示结束该方法
3. 方法名
小写大写大写大写
4. 形参列表:可以声明0-n个形参
格式:数据类型1 形参1,数据类型2 形参2,...,数据类型3 形参3
方法的使用
成员函数可以调用当前类的属性和方法
特别的:方法A中又调用自己(A),即递归方法
方法中不能定义方法:比如main里面不能写新函数(方法)(静态除外),但是可以调用函数 #tips
方法内调用本类的方法时,可以直接写方法名或者this.的形式
方法内调用其他类的static方法,类名.方法名的形式
方法内调用父类的方法,super.的形式
1. 求圆的面积
2. 对象数组Student类
注意内存解析 #tips
只是创建了一个元素类型是引用类型(Student)的数组str,只是为该数组分配了空间,但是数组的元素(每个元素是一个Student类型的对象)还没有创建,所以在for循环中要先new每一个元素 #tips
讲解图示举例
引用变量只能存储地址或者null
空指针异常:.前面的内容如果是null,就会空指针 #tips
注意排序中的换序:根据成绩排序,但是交换的整个学生Student对象(数组里元素),而不要交换成绩!!另外这里的temp的定义也要和Student类型统一,所以Student temp; #tips
可以在main方法之外,测试类(java本类)之内定义函数封装功能,在main函数里创建本类的对象,通过该对象调用写好的方法,能使程序更简洁 #tips
方法之前最好写一下注释/**,便于以后查看
3. 匿名对象的使用 #tips
我们创建的一个对象,没有显式赋值给一个变量名
特征:匿名对象只能用一次
开发中的使用(不用再定义对象名,有时候比较方便)
4. 自定义工具类
调用的时候要先创建工具类的对象
六、再谈方法
(一)方法的重载 (注意重载和重写的区别,用重写比较多)#tips
同名方法:参数类型或者参数个数不同(API好多同名的)
两同一不同
同一个类,相同方法名
参数不同
跟方法的权限修饰符,返回值类型,形参变量名,方法体都没有关系
通过对象调用方法时,如何确定一个指定的方法
看方法名
看参数列表
(二)可变个数形参的方法(替换了旧版本的数组)
具体使用
可变个数形参的格式:数据类型... 变量名
可变形参的方法和同一个类中的同名函数构成重载
可变形参必须声明在末尾
可变个数形参在方法的形参中,最多只能声明一个可变形参
和数组的方式一样,编译器认为是同一个函数;可以和数组一样获取下标值
(三)方法参数的值传递机制
关于变量的赋值(equals和==)
基本数据类型:变量保存的数据值
引用数据类型:地址值
方法形参的传递机制:值传递
参数类型:
1. 形参:方法声明的小括号内
2. 实参:调用时实际传递给形参的值
(java中的形参传递)值传递机制
如果是基本数据类型:实参赋给形参的是真是存储的数据值
如果是引用数据类型:实参赋给形参的是真是存储的地址值
举例说明(交换)
错误版:体现是基本数据类型的值传递
传递的是真实的值,交换的是形参,实参其实没变;然后交换函数结束后,形参出栈销毁,等用没换
正确版:体现的是引用数据类型的值传递(用数组的方式):赋的是地址,交换操作是对地址对应的真实对象的操作
传递的是地址值,所以在函数里面处理的是堆空间的实际的数组
例题 #题目
(四)递归方法
特点
调用自身
隐式的循环,不需要循环控制
一定要向一直方向递归,否则就死循环了
终止条件:没有再调用自身的时候就结束了
求和
数列问题
斐波那契数列 #题目
斐波那契数列的应用
n阶台阶的走法
汉诺塔
快排
(五)复习 #复习
什么是方法的重载
两同一不同:同一个类,相同方法名;参数列表不同(参数名不一样步算)
和权限修饰符,参数返回值没有关系
如何调用确定的方法:方法名+确定的参数列表
说明Java方法中的参数传递机制的具体体现
基本数据类型:数据值
引用数据类型:地址值(含有变量的数据类型)
Person p1 = new Person();
User u1 = p1; //编译错误:(逆向思维,反证法)
能赋值的前提时类型一致
成员变量和局部变量的异同
return关键字的使用
结束方法(后面不能有东西),不论return在方法的什么位置,只要return就结束方法
有返回值的情况
代码的内存解析
内存的结构:栈(局部变量);堆:new的结构(对象,数组)
变量:成员变量 & 局部变量(栈)
七、OOP特征一:封装与隐藏
1. 封装性的引入
对属性赋值加入额外的限制条件,没法在属性声明时体现,so - >方法内实现限制;
避免使用对象.属性对属性赋值,通过本类的方法调用属性,所以将属性声明为私有private,此时legs是体现出封装性了(隐藏了)
2. 封装性的体现(面试)
举例:类的属性私有化(private),同时提供公共(public)的方法获取(get)和设置(set)属性
1. 如上
2. 对类内使用的方法(不对外暴露的私有的方法)
3. 单例模式(构造器私有化):只有一个对象实例
4. 如果不希望类在包外被调用,可以将类定义为缺省的
3. 封装性的体现需要权限修饰符
1. java规定的四种权限修饰符(由小到大)
2. 四种权限修饰符可以修饰类和类的内部结构:属性,方法,构造器,内部类
3. 具体的,修饰类的话只能是public或者缺省
4. 测试
总结:四种权限修饰修饰类和类的内部结构,体现了类和类的内部结构被调用的时候的可见性的大小
八、类的成员之三:构造器(Constructor)
1. 构造器的作用:
1. 创建对象
2. 初始化对象的信息
3. 举例
2. 说明:
如果没有显式定义类的构造器的话,系统默认提供一个空参的构造器
定义构造器的格式:权限修饰符 类名 (形参列表 ){}
最好不理解为方法Method(构造器没有返回值)
可以定义多个构造器
一旦显式定义了构造器之后,系统就不再提供默认的构造器了
一个类中至少会有一个构造器(可能是默认的,也可能是自己写的)
3. 总结属性赋值的先后顺序(初始化)
属性赋值位置:
1. 默认初始化值(成员变量和局部变量的区别之一:成员变量有默认初始化值)(比如int型默认1,Boolean型默认false)
2. 显式初始化:int age = 1;(声明属性的时候顺便赋值,修改了默认初始化值)
3. 构造器中赋值
4. 通过"对象.方法"或'对象.属性‘赋值
以上操作的先后顺序1234
4. JavaBean
一种Java语言写成的可重用组件
符合下列要求的标准Java类
类是公共的
有一个无参的公共的构造器
有属性,且有对应的get、set方法
用户可以使用JavaBean将功能、处理、值、数据库访问和其他任何可以 用Java代码创造的对象进行打包,并且其他的开发者可以通过内部的JSP 页面、Servlet、其他JavaBean、applet程序或者应用来使用这些对象。用 户可以认为JavaBean提供了一种随时随地的复制和粘贴的功能,而不用关 心任何改变
九、关键字:this
1. this调用属性和方法
this理解为当前对象
通常省略this.
特殊情况下不能省略:方法或者构造器的形参和类的属性重名
this.属性表明此变量是属性,不是形参
2. this调用构造器(避免代码冗余,借用其他构造器的逻辑)
2.1 这里的this是指正在创建的对象
2.2可以在类的构造器中使用"this(形参列表)"的方式,调用本类中重载的其他的构造器
2.3 构造器中不能通过"this(形参列表)"的方式调用自身构造器
2.4 this调用的哪一个构造器可以通过参数判断(重载)
2.5 必须声明在当前构造器的首行(先执行别人);并且只能调用一个其他构造器
十、关键字:package和import
更好的管理项目中的类的管理
使用package声明类或接口所属的包,声明在源文件的首行
包属于标识符,小写
每一个.都是一层目录
同一个包下不可以重名;不同的包下可以同名
包下可以有子包
1. 在源文件中使用import显式的导入指定包下的类或接口
2. 声明在包的声明和类的声明之间
3. 如果需要导入多个类或接口,那么就并列显式多个import语句即可
4. 举例:可以使用java.util.*的方式,一次性导入util包下所有的类或接口
5. 如果导入的类或接口是java.lang包下的(核心包,比如System类或者String类),或者是当前包下的,则可以省略此import语句。
6. 如果在代码中使用不同包下的同名的类,那么就需要使用类的全类名的方式指明调用的是哪个类。
7. 如果已经导入java.a包下的类。那么如果需要使用a包的子包下的类的话,仍然需要导入
8. import static组合的使用:调用指定类或接口下的静态的属性或方法
十一:练习 & 项目
银行练习
错误点
空指针异常
原因:数组还没有创建,此时的默认初始值是null,要保证调用addCustiomer之前把数组new出来
解决方法(数组的初始化写到构造器中或者在定义属性的时候显式赋值)
客户信息管理项目
MVC架构
model:Customer为实体对象,用来封装客户信息(一个JavaBean)
封装用户基本信息
提供各属性的get和set方法
提供需要的构造器(一般是一个空参构造器和一个全参构造器)
view:CustomerView为主模块,负责菜单的显示和处理用户操作
分支主题
controller:CustomerList为Customer对象的管理模块,内部用数组管理一组Customer对象,并提供相应的添加、修改、删除和遍历方法,供CustomerView调用
封装用来保存客户对象的数组;记录保存客户的数量
提供构造器
对Customer对象数组进行增删改查操作,并且展示用户列表
快捷键:
补全代码:alt + /
快速修复:ctrl + 1
批量导包:ctrl + shift + O
单行注释:ctrl + /
多行注释:ctrl + shift + /
复制指定行的代码:ctrl + shift + down/up
删除指定行的代码:ctrl + d
上下移动代码:alt + up / down
切换上下行:shift + 回车 / ctrl + shift + 回车
查看源码:ctrl + 具体的结构 / ctrl + shift + t 直接搜索某一个指定结构
退回到前一个编辑的页面:alt + 左 / 右
光标选中指定的类,查看继承树的结构 :ctrl + t
反撤销:ctrl + y
格式化代码:ctrl + shift + f
选中数行,整体后移:tab;整体前移:shift + tab
在源码中找指定的方法:ctrl + O 可以搜索
批量修改指定的变量名、方法名、类名:alt + shift + r或者ctrl + f,查找后修改
大小写的切换:ctrl + shift + s / y
调出getter、setter、构造器结果:alt + shift + s
显式当前文件的属性:alt + enter 或者直接右键
快速查找:ctrl + f查找;或者选中之后ctrl + k(便于追踪变量)
快速关闭当前窗口:ctrl + w
关闭所有窗口:ctrl + shift + w
查看指定的结构使用过的地方:ctrl + shift + g
直接定位到当前的首行:home
直接定位到当前的末尾:end
面向对象编程(中)
一、OOP特征之二:继承性
(一)继承性的好处
减少代码的冗余
便于功能的拓展
为多态性提供了前提
(二)继承性的格式
class A extends B
A子类 subclass 派生类
一旦继承之后,子类就获取了所有父类的属性和方法,特别的,父类中的private的属性和方法,子类继承后仍然全部获取了父类的这些私有的结构,只是由于封装性的影响,使得子类不能直接调用父类的这些私有结构(可以用setter和getter方法)
私有方法可以通过别的公有方法体现出继承,在继承的父类的公有方法里可以调用到继承到的私有方法)
B父类Superclass超类 基类
子类继承父类之后还可以自己声明和定义自己的属性和方法,实现功能的拓展
子类的功能更加强大
(三)继承性的规定
一个父类可以有多个子类
一个子类只能有一个父类(Java中的类的单继承),接口解决了单继承的局限
可以多层继承
子类直接继承的父类:直接父类
子类间接继承的父类:间接父类
子类继承了直接父类和间接父类的所有结构
(四)其他
如果没有显式声明父类,则此类继承于java.lang.Object类
所有的java类(除了Java.long.Object);类之外,都直接或者间接继承于java.lang.Object类
意味着所有的java类都有Object类的功能
二、方法的重写(override)
(一)定义:
子类继承父类之后,可以对父类中同名同参数的方法进行覆盖操作(子类和父类,而重载是在本类)
(二)应用:
重写之后,当创建子类对象以后,通过子类对象调用父类的同名同参数的方法时,实际执行的是子类重写的父类的方法
(三)* 重写的规定:
注意:
方法的声明:权限修饰符 返回值 方法名(形参列表){//方法体}
约定俗成:子类中的方法:重写的方法;父类中的:被重写的方法
子类的权限要更广,但返回值更紧
子类和父类中的同名同参数的方法
要么都是static
要么都是非static(非static才能重写)
(不能重写父类的static方法,也不能在重写的过程中声明为static类型)
1. 方法名和形参列表:完全一样
2.权限修饰符:子类的权限不小于父类的(不能变得更紧——覆盖的话权限要大于等于之前的,摊煎饼)
子类不能重写父类中声明为private的方法
3. 返回值类型
如果父类的是void,则子类的只能是void(相同)
父类的:基本数据类型A,子类的也必须是A类型(相同)
父类的:引用数据类型A,子类的可以是A或者A向下(A的子类)
4. 抛出的异常类型:子类不能抛出比父类更大的异常(小于等于,可以是父类的异常或者父类异常的子类),若父类没有抛异常,子类自然不能抛异常
5. 只有非static才能重写
(面试题)重写和重载的区别:
细节区别:(概念)
重载:方法名相同,参数列表不同;在同一个类中
重写:参数列表和方法名都一样;有子父类关系,在不同的类中
从编译和运行的角度看:
重载,是指允许存在多个同名方法,而这些方法的参数不同。编译器根据方法不 同的参数表,对同名方法的名称做修饰。对于编译器而言,这些同名方法就成了 不同的方法。它们的调用地址在编译期就绑定了。Java的重载是可以包括父类和子类的,即子类可以重载父类的同名不同参数的方法。 所以:对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法, 这称为“早绑定”或“静态绑定”;
而对于多态,只有等到方法调用的那一刻,解释运行器才会确定所要调用的具体 方法,这称为“晚绑定”或“动态绑定”。
三、四种访问权限修饰符
本类中可以用,同一个包内非本类不可以用类名.来调用
本包内可以用
不同包的子类(用的少)
只要是在一个项目下就都可以用
四、关键字:super
1. 调用属性:
子类和父类定义了同名的属性时(很少出现),默认调用自己的,若调用父类的则要用super关键字
2. 调用方法:
子类重写了父类的方法(方法重名),想在子类的方法中调用父类中被重写的方法时,必须显式使用”super.方法“表明调用的是父类的
用了super先去直接父类找,如果没找到,接着往上找
不用super默认调用自己的
3. 调用构造器
3.1 可以在子类的构造器中显式的使用”super(形参列表)“的方式调用父类中声明的指定的构造器
3.2 ”super(形参列表)“的方式必须声明在子类构造器的首行
3.3 我们在类的构造器中,针对this(形参列表)->调用本类的重载的其他构造器和super(形参列表)->调用父类的构造器,只能使用一个
3.4 构造器的首行没有显式声明this或者super时,默认super()空参构造器;
为了尽量避免报错,一般都要提供空参构造器 #tips
3.5 在类的多个构造器中,至少有一个构造器使用了super(形参列表)调用父类中的构造器
4. this和super的区别
五、子类对象实例化过程
1. 从结果上看(继承性):
子类继承父类之后获取了父类声明的属性和方法
创建的子类的对象在堆空间中会加载所有父类声明的属性
2. 从过程上看:
通过子类的构造器创建子类对象时,会直接或间接地调用其父类的构造器,进而调用父类的父类的构造器,直到调用了java.lang.Object的空参构造器为止
正因为加载了父类的构造器,所以才看到内存中有父类的结构,子类对象才可以考虑进行调用
虽然创建子类对象调用了父类的构造器,但是就创建了一个对象
3. 普通账户和可透支账户实例
1. 关于可透支账户CheckAccount 的构造情况
用了父类的有参构造器,再加上this.自己新加的属性构造一下
2. 取款方法的重写
一般账户余额不会有set方法,只有get方法,此时可以用父类的withdraw方法(有基本的关于balance的冲减情况);注意使用到透支金额的分支条件下的方法体,先冲减overdraft再把balance置为0;和日常思维顺序不一致
六、OOP特征三:多态性
(一)理解:一个事物的多种形态
(二)何为多态性:
对象多态性:父类的引用person1 / person2 指向子类的对象 huozhe 子类的对象赋给父类的引用
声明为父类的类型,实际引用的是子类的对象
(三)应用:(虚拟方法调用)
一些:
当调用子类父类的同名同参数的方法时,调的是子类重写的方法
不能调用子类独有的方法,因为编译时要查看引用变量所声明的类(父类)中是否有所调用的方法或属性
(虚拟方法调用)有了对象的多态性之后,在编译期只能调用父类声明的方法,但是运行期,实际执行的是子类重写的父类的方法
总结:编译看左边,执行看右边
(四)多态性的使用
1. 前提
有类的继承关系
要有方法的重写
方法声明的形参类型为父类类型,可以使用子类的对象作为实参调用该方法
Animal animal = new Dog() : 父类的引用指向子类的对象 / 子类的对象赋值给父类的引用
没有多态性的话,function方法参数只能是父类Animal对象, 如果想调用狗和猫的shout和eat方法,则必须分别在猫和狗类里写重载方法function,非常麻烦;有了多态性,在运行的时候调用子类重写的相应的方法就可以了
(五)虚拟方法调用
子类定义了与父类同名参数的方法,在多态情况下,此时父类的方法称为虚拟方法,父类根据赋给他的不同子类对象,动态调用属于子类的该方法。这样的方法调用在编译器是无法确定的。
编译时类型和运行时类型
多态性是运行时行为
(六)补充:关于向上转型和向下转型
1. instanceOf
引入(为什么使用向下转型)
需要说明,有了子类对象的多态性之后,内存中实际已经加载了子类特有的属性和方法,但是由于对象声明为父类的类型,导致编译时之能调用(识别)父类声明的属性和方法,子类的特有的属性和方法不能调用
如何才能调用子类特有的属性和方法(让编译器看到的person2不是Person类型而是Student类型才可以)
解决(如何使用向下转型)
向下转型——使用强制类型转换符
解释:多态——向上转型;instanceOf——向下转型
instanceOf的使用
为了保证使用向下转型不出现classCastException异常,先做instanceOf的类型判断,返回true继续强转,返回false不继续
a instanceOf A:判断对象a是不是类A的实例,如果是返回”true“,不是返回”false“
使用实例:
另外,如果a instanceOf A返回true,则a instanceOf B也返回true的话,那么B是A的父类
没有关系的类不能强转,必须有子父类关系
是子类类型则一定是父类类型
面试题目:如何实现向下转型
使用强转符()
可能出现ClassCastException异常
所以使用instanceOf进行向下转型前判断
变量名(对象) instanceOf 类名
(七)多态性练习
1. 小笔试题:
若子类重写了父类的方法,就意味着子类定义的方法彻底覆盖了父类的同名方法,系统不可能把父类的方法转移到子类里
对于实例变量不存在这种现象,即使子类里定义了与父类完全相同的实例变量,这个实例变量依然不可能覆盖父类里的实例变量
变量不存在重写override现象
2. 几何图形求面积coding
多态性体现
一些要注意的问题
3. 面试题:
多态是编译时行为还是运行时行为
运行时行为
证明:随机数
如何理解多态性
目的:实现代码的通用性
应用比如:
Object类中的equals()方法)
JDBC:通过Java操作数据库
获取数据库连接
增删改查数据库
抽象类和接口的使用肯定体现了多态性(抽象类、接口不能实例化)
七、Object类的使用(java.lang.Object)
(一)说明
Object类是所有Java类的根父类
如果类的声明中没有extends其他的类,那么该类的父类就是Object类
(二)Object类中的属性和方法
1. 没有属性;只有一个空参的构造器
2. 主要结构
另外
finalize():垃圾回收器:垃圾回收是一个线程(一个Java程序至少有三个线程)
如果堆空间中的对象实体没有对应的引用之后,Java垃圾回收
System.gc():通知系统垃圾回收(强制回收);垃圾回收机制回收任何对象之前,先调用finalize 方法,永远不要主动调用finalize方法
面试题:final,finally和finalize的区别
clone():克隆对象
getClass():获取当前对象的类
HashCode():返回当前对象的哈希值
wait():线程通信的时候讲
notify
notifyall
3. 方法介绍
3.1 equals:两个对象是不是相等的
3.1.1 ==的使用回顾
运算符
可以使用在基本数据类型和引用数据类型变量中;用于比较的两者可以是不同类型的(Boolean除外)
如果是比较基本数据类型:比较保存的数据(不一定类型相同)
如果比较引用数据类型:比较地址值(即两个引用/变量名是否指向同一个对象实体)
3.1.2 equals()
方法(非运算符),只适用于引用数据类型;x.equals(null)返回false
关于== 和equals
Object类中equals的定义跟”=="是相同的,源码如下
而String,Date,File,包装类等都重写了Object类的equals()方法,重写之后,比较的不是两个引用的地址是否相同,而是比较两个对象的是实体内容是否相同
3.1.3 自定义类如何重写equals()方法
手写自己的equals()方法示例
先判断地址是否相同(==判断)
相同:直接返回true
不同,判断比较对象是否是本类
是:比较每个属性
返回是否都相同
不是:返回false
自动生成自己的equals()方法
alt + insert
3.1.4 面试题:== 和equals 区别
大纲:Object中如何定义的 ----》有些类是如何重写的----》我们自己在类里重写
(1)==是运算符, 既可以比较基本类型也可以比较引用类型。对于基本类型就是比较值,对于引用类型 就是比较内存地址
(2)equals()它是属于java.lang.Object类里面的方法,只能比较引用数据类型,如果该方法没有被重写过默认也 是==;
(3)我们可以看到String等类的equals方法是被重写过的,而且String类在日常开发中用的比较多,久而久之,形成了equals是比较值的错误观点
(4)具体要看自定义类里有没有重写Object的equals方法来判断,通常情况下,重写equals方法,会比较类中的相应属性(具体的实体内容)是否都相等
3.1.5 equals()练习中的一些问题
String类型的变量赋值方法不同,结果不同
自定义类中有自定义类的话,equals()方法比较过程中也要调用自定义类的重写后的equals()方法
3.2 toString():转换为字符串(经常需要重写自定义类的toString()方法)
使用
1. 当输出一个对象的引用时,实际上就是调用当前对应对象的toString()方法
Object源码中的toString()
println()源码(一部分)
进入valueOf(调用了toString())
2. String,Date,File,包装类等都重写了Object的toString(),使得在调用对象的toString()方法时返回“实体内容”信息
3. 自定义类也可以重写toString(),调用该方法时返回实体内容
4. 可以自动生成
(三)Object类的练习
八、包装类的使用
(一)Java中的单元测试
步骤
测试方法
结果
(二)包装类
1. 定义:针对八种基本数据类型定义了相应的引用类型,有了类的特点,就可以调用类的方法,Java才是真的面向对象;为了使基本数据类型有类的特征
2. 包装类、基本数据类型和String的转换
2.1 包装类和基本数据类型(自动装箱,自动装箱)
自动装箱和自动拆箱(替代用构造器转化和用.xxValue())
可以近似看成一个整体
2.2 包装类和基本数据类型 --> String (调用String类的valueOf())
方式一:和字符串连接
方式二:调用String.valueOf(Xxx xxx)
2.3 String --> 基本数据类型和包装类(调用包装类.parseXxx)
包装类.parseXxx(str)
转换时可能报NumberFormatException异常
面试题
三元运算符两个分支的类型必须一致,不一致的话会自动类型转换
IntegerCache问题
包装类练习题
题目:利用Vector代替数组处理:从键盘读入学生成绩(以负数代表输入结束),找出最高分,并输出学生成绩等级;若与最高分相差10分内:A等;20分内:B等;* 30分内:C等;其它:D等
补充:向量类java.util.Vector可以根据需要动态伸缩
Vector只定义了Object类型的形参 --> 基本数据类型先转化为包装类 --- >进而可以使用Vector
创建Vector对象:Vector v=new Vector()
给向量添加元素:v.addElement(Object obj)
obj必须是对象
取出向量中的元素:Object obj=v.elementAt(0)
注意第一个元素的下标是0,返回值是Object类型的
计算向量的长度:v.size();
截图
main
自定义功能函数
面向对象编程(下)
一、关键字:static(静态只能调静态)
(一)简介
static静态的
static可以修饰属性,方法,代码块,内部类(不修饰构造器)
(二)* 修饰属性:(static修饰的属性——静态变量)
1. 属性按照是否被static修饰可以分为
静态属性(静态变量 / 类变量)
创建多个某类的对象,多个对象共享一个静态变量,一改全改。当通过某个对象修改过静态变量时,会导致其他对象调用该静态变量时,发现该静态变量已经被修改过(一改全改)
非静态属性(实例变量)
创建了类的多个对象时,每个对象都独立拥有一套类中的非静态属性,当修改其中一个对象的某个非静态属性时,不会改变其他对象中该属性的值
2. static修饰属性的其他说明(相比于普通的属性有什么特别)题
2.1 加载:静态变量随着类的加载而加载
2.2 调用:静态变量的加载早于对象的创建,new对象之前就能用静态变量,”类名.静态变量名“(实例变量不能通过类调用)
2.3 独立性:由于类只会加载一次,所以静态变量在内存中只存在一份,存在方法区的静态域中
2.4 静态属性一般不在构造器里初始化
3. 静态属性(变量)举例
System.out,Math.PI
4. 加载的内存解析
(三)*修饰方法
1. 加载时间;随着类的加载而加载,可以通过“类.静态方法”调用,或者”对象.静态方法“
2. 调用:静态方法中只能调用静态的方法或者属性,而非静态方法可以调用both静态and非静态(跟他们的生命周期有关)(晚出生的可以调用早出生的,早出生的不能调用晚出生的)
3. 静态的方法不能使用super和this
生命周期的角度分析
(四)修饰代码块
(五)修饰内部类
(六)如何确定一个属性 / 方法要声明为static
属性:
可以被多个对象共享的,不会随着对象的不同而不同
类中的常量也常常声明为static(final更严格,不妨加个static)
方法:
1. 操作静态属性的方法
举例:单例模式
2. 工具类中的方法(没有必要造对象去调方法了):直接类名.静态工具方法名就可以
举例:Collection里的静态方法(工具类)
练习题
圆
声明静态变量
调用静态变量 / 静态方法
银行账户
(七)单例模式
1. 设计模式(常见的23种)
是在大量的实践中总结和理论化之后优选的代码结构、编程风格、 以及解决问题的思考方式
设计模免去我们自己再思考和摸索。就像是经典的棋谱,不同的棋局,我们用不同的棋谱
”套路”
2. 单例模式
2.1 定义:
就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法
如果我们要让类在一个虚拟机中只能产生一个对象,我们首先必须将类的构造器的访问权限设置为private,这样,就不能用new操作符在类的外部产生类的对象了,但在类内部仍可以产生该类的对象
因为在类的外部开始还无法得到类的对象,只能调用该类的某个静态方法以返回类内部创建的对象, 静态方法只能访问类中的静态成员变量,所以,指向类内部产生的该类对象的变量也必须定义成静态的
2.2 优点:
单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可 以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决
2.3 举例:
java.lang.Runtime
2.4 单例模式的应用场景
网站的计数器
一般也是单例模式实现,否则难以同步
应用程序的日志应用
一般都使用单例模式实现,这一般是由于共享的日志 文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加
数据库连接池
数据库连接是一种数据库资源
项目中,读取配置文件的类,一般也只有一个对象
没有必要每次使用配置 文件数据,都生成一个对象去读取
Application
Windows的Task Manager (任务管理器)
Windows的Recycle Bin (回收站
在整个系统运行过程 中,回收站一直维护着仅有的一个实例。
3. * 如何实现单例模式(要会写)
1. 私有化类的构造器
2. 内部创建 / 声明 私有的类的对象(static)
3. 提供公共public static 方法返回类的对象
饿汉式
懒汉式
4. 区分懒汉式 / 饿汉式
饿汉式:(面试中直接写这个吧)
坏处,对象加载时间过长
好处:线程安全的
懒汉式:
坏处:目前的写法线程不安全 ---> 多线程学习时再修改
二、理解main方法的语法
1. main()作为程序的入口
2. 也是一个普通的静态方法
只能调用静态的,所以自己写了方法要先new本类的对象才能在main里用
3. main()也可以作为我们与控制台交互的方式
4. 小结:一叶知秋
public static void main(String[] args){ }
权限修饰符:封装性
修饰符:static / final / abstract / native 可以修饰方法
返回值类型: void / 有返回值(return)
方法名:命名规则
形参列表:重载 / 重写;参数的值传递机制(数据值 / 地址值);体现对象的多态性(代码的复用性)
方法体:体现方法的功能
三、类的成员之四:代码块(使用频率不高)
1. 代码块的作用:初始化类 / 对象信息
代码块如果有修饰的话,只能时static
2. 分类
2.1 静态代码块(相比于非静态代码块,静态代码块先执行,一般就写一个)
内部可以有输出语句
随着类的加载而 执行(静态方法随着类的加载而加载,但是需要用类来调用),且只能执行一次
作用:初始化类的属性:初始化类的static
可以定义多个,按照声明的顺序
代码块内只能调用静态的xxx(因为随着类的加载而执行)
2.2 非静态代码块(和对象相关)
随着对象的创建而执行
每创建一个对象都会执行一次
作用:可以在创建对象时,对对象的属性进行初始化
初始化属性的位置
1. 默认
2. 显式 / 代码块(写的先后的问题)
3. 构造器
4. 点
可以调用静态 & 非静态
加载顺序:
实例化子类对象时,涉及父类、子类中的静态、非静态代码块、构造器的加载顺序:
由父及子,静态先行(父类静态代码块 ---》子类的静态代码块 ---》父类的非静态代码块,构造器 ----》子类的非静态代码块,构造器
四、关键字final
(一)可以修饰的结构:
1.类
不能被继承:提高安全性,提高程序的可读性
比如String类,System类,StringBuffer类
2.方法
不能被子类重写
比如Object类中的getClass()
3.变量(用的多)
3.1 特点:
即常量,通常与static关键字一起用:static final 全局常量(接口里全是全局常量)
变量名全部大写,且只能被赋值一次
3.2 修饰的成员变量
位置
如果大家都一样且不变:显式赋值
如果不变但是每个都不一样:比如身份证号:构造器里
如果显式赋值时有好多功能:代码块里
final标记的成员变量必须在3. 声明时或者在1.每个构造器中或2.代码块中显式赋值,然后才能使用
修饰的局部变量
尤其是修饰形参时:表明该形参是一个常量,调用此方法时,给常量形参赋一个实参,一旦赋值以后,就只能在该方法体使用此形参,但不能重新赋值
(二)面试题
第一个:注释对
第二个:没问题,o没有重新赋值就好,i是变量可以变
五、抽象类和抽象方法
(一)abstract可以修饰的结构
1. 修饰类
1.1 此类不能实例化
1.2 抽象类一定有构造器(所有的类都有构造器),便于子类实例化时调用(涉及:子类对象的实例化过程)
1.3 开发中都会提供抽象类的子类,让子类对象实例化完成相关操作
2. 修饰方法
2.1 抽象方法只有方法的声明,没有方法体
2.2 包含抽象方法的类,一定是一个抽象类;反之一个抽象类可以没有抽象方法
2.3 若子类重写了所有父类(包含间接父类)中的所有抽象方法之后,此子类才能实例化
2.4 若子类没有全部重写父类的抽象方法,则此子类仍然是一个抽象类,要用abstract修饰
3. abstract使用的注意点
3.1 abstract不能修饰属性、构造器等结构
3.2 abstract不能用来修饰私有方法、静态方法、final方法、final的类
(二)抽象类的匿名子类
创建Person的匿名子类的 匿名对象 (一般作为方法的参数使用) 直接写方法
(三)多态的应用:模板方法的设计模式(TemplateMethod)
1. 介绍:
抽象类体现的就是一种模板模式的设计
抽象类作为多个子类的通用模板
子类在抽象类的基础上进行扩展、改造
但子类总体上会保留抽象类的行为方式
2. 解决的问题
当功能内部一部分实现是确定的,一部分实现是不确定的。这时可以 把不确定的部分暴露出去,让子类去实现
在软件开发中实现一个算法时,整体步骤很固定、通用, 这些步骤已经在父类中写好了;但是某些部分易变,易变部分可以抽象出来,供不同子类实现。这就是一种模板模式
实例:
抽象模板类
实现抽象模板类(质数)
测试
(四)抽象类小练习
注意点
初始化的时候如果类型是引用数据类型,要new一下
声明属性的时候,如果属性是引用数据类型的数组,则声明只是声明了一个数组,该数组里的元素(引用数据类型)并没有被创建 ----> 比如如下为Employee是一个抽象类也没关系
抽象类的一切(属性、抽象方法、非抽象方法)生来就是服务于子类的,否则没有存在的意义,因为抽象类压根不能创建对象。这里的抽象类Employee的toString()的重写就完全是为了子类方便而改的格式
日期类
构造器的声明和使用
六、接口
(一)引入:为什么用接口
需求:
一方面,有时必须从几个类中派生出一个子类,继承它们所有的属性和方 法。但是,Java不支持多重继承。有了接口,就可以得到多重继承的效果
另一方面,有时必须从几个类中抽取出一些共同的行为特征,而它们之间又 没有is-a的关系,仅仅是具有相同的行为特征而已。例如:鼠标、键盘、打 印机、扫描仪、摄像头、充电器、MP3机、手机、数码相机、移动硬盘等都 支持USB连接
解释:
接口就是规范,定义的是一组规则,体现了现实世界中“如果你是/要...则 必须能...”的思想。继承是一个"是不是"的关系,而接口实现则是 "能不能" 的关系
接口的本质是契约,标准,规范,就像我们的法律一样。制定好后大家都要遵守,不能改
(二)接口的使用
1. 介绍
1.1 用interface定义
1.2 Java中,接口和类是两个并列的结构
1.3 接口、类之间的关系
1.3.1 接口和接口:继承
1.3.2 类和类:继承
1.3.3 类和接口:实现
2. 定义接口 ---> 定义接口中的成员
2.1 JDK7及以前:只能定义全局常量和抽象方法
>抽象方法:public abstract(也可以省略)
2.2 JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法default关键字(可以省略public)
2.2.1 静态方法:
知识点1:接口中定义的静态方法,只能通过接口来调用
2.2.2 默认方法:
知识点2:通过实现类的对象,可以调用接口中的默认方法,如果实现类重写了接口中的默认方法,调用时,仍然调用的是重写以后的方法
知识点3:如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的默认方法,那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数的方法。--> 类优先原则
知识点4:如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,那么在实现类没有重写此方法的情况下,报错。-->接口冲突。这就需要我们必须在实现类中重写此方法
知识点5:如何在子类(或实现类)的方法中调用父类(super)、接口(类名.super. 方法)中被重写的方法
2.3 接口不能定义构造器!意味着接口不能实例化
3. 实现接口:接口通过类实现(implements)来使用
3. 1 如果实现类覆盖了接口中的所有抽象方法,则此实现类就可以实例化
3. 2 如果实现类没有覆盖接口中所有的抽象方法,则此实现类仍为一个抽象类
3.3 一个类可以实现多个接口 ---- >弥补了Java类单继承的缺陷
比如子弹bullet既可以飞又可以攻击
3.4 先写extends,后写implements
3.5 也可以创建接口的匿名实现类 的 匿名对象(匿名的四种组合)
4. 接口的意义
4.1. 接口的具体使用,体现多态性
4.2. 接口,实际上可以看做是一种规范
接口定义了一种规范,这里是跟电脑传输数据的规范,即Usb,传输数据必须实现Usb接口,把规范明确一下
4.3. 面试题:抽象类与接口有哪些异同?
相同点:不能实例化,都可以被继承
不同点:
抽象类:有构造器;单继承
接口:不能声明构造器;多继承
jdk的更新接口越来越像类
* 5. 接口的应用(两种设计模式)
5.1 代理模式
概述
代理设计就是为其他对象提供一种代理以控制对这个对象的访问
应用场景
安全代理:屏蔽对真实角色的直接访问
远程代理:通过代理类处理远程方法调用(RMI)
延迟加载:先加载轻量级的代理对象,真正需要再加载真实对象
比如你要开发一个大文档查看软件,大文档中有大的图片,有可能一个图片有 100MB,在打开文件时,不可能将所有的图片都显示出来,这样就可以使用代理 模式,当需要查看图片时,用proxy来进行大图片的打开
分类:
静态代理:(静态定义代理类)
动态代理:(动态生成代理类)
JDK自带的动态代理,需要反射等知识
5.2 工厂模式
6. 关于接口的面试 / 笔试题
6.1 全局常量的问题
七、类的成员之五:内部类
(一)定义
Java中允许将一个类A声明在另一个类B中,则类A就是内部类,类B称为外部类
(二)分类
1. 成员内部类(静态 / 非静态)
作为类
类内可以定义属性、方法、构造器等
可以被final修饰,表示此类不能被继承。言外之意,不使用final,就可以被继承
可以被abstract修饰
作为成员
调用外部类的结构
可以被static修饰(一般的类只能被public和
可以被4种不同的权限修饰
2. 局部内部类(方法内、代码块内、构造器内)
(三)三个问题
如何实例化成员内部类的对象
如何在成员内部类中区分调用外部类的结构
开发中的使用(方法里的局部内部类)
异常处理
一、异常概述和异常体系结构
java.lang.Error:Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源 耗尽等严重情况,一般不编写针对性的代码进行处理
栈溢出:StackOverflowError
递归调用无终点
堆空间溢出:OOM
java.lang.Exception:: 其它因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理
编译时异常(checkde):(有红色x)(非RunTimeException下的)
IOException
FileNotFoundException
ClassNotFoundException
运行时异常(写代码不会报错没有x)(RunTimeException下的)
NullPointerException
ArrayIndexOutOfBoundsException
ClassCastException:类型转换异常
NumberFormatException
InputMismatchException:Scanner时输入不匹配(int 和 String)
ArithmeticException:算数异常(比如除以0)
面试题:常见的异常有哪些,举例说明
二、常见的异常
编译时异常(非RunTimeException)
FileNotFoundException没找到文件
运行时异常(RunTimeException下的)
ArithmeticException
除以0了
InputMismatchException
输入不匹配(要求int输入String)
ClassCastException
IndexOutOfBoundsException
三、异常处理机制(两种:自己处理;甩锅))
(一)引入
在编写程序时,经常要在可能出现错误的地方加上检测的代码, 如进行x/y运算时,要检测分母为0,数据为空,输入的不是数据 而是字符等。过多的if-else分支会导致程序的代码加长、臃肿, 可读性差。因此采用异常处理机制
(二)异常的处理:抓抛模型
过程一:"抛"(异常的产生)
程序在正常执行的过程中,一旦出现异常,就会在异常代码处生成一个对应异常类的对象,并将此对象抛出
一旦抛出对象以后,其后的代码就不再执行
关于异常的产生:
① 系统自动生成的异常对象
② 手动的生成一个异常对象,并抛出(throw)
过程二:"抓"(异常的处理)
可以理解为异常的处理方式
① try-catch-finally
② throws
(三)方式1----try-catch-finally
1. 基本结构
2. 具体说明
2.1 花边儿
finally可选(有点像switch里的default)
try-catch-finally结构可以嵌套
使用try-catch-finally处理编译时异常,是得程序在编译时就不再报错,但是运行时仍可能报错。相当于我们使用try-catch-finally将一个编译时可能出现的异常,延迟到运行时出现
开发中,由于运行时异常比较常见,所以我们通常就不针对运行时异常编写try-catch-finally了(自己改代码)。针对于编译时异常,我们说一定要考虑异常的处理
2.2 过程
2.2.1 使用try将可能出现异常代码包装起来,在执行过程中,一旦出现异常,就会生成一个对应异常类的对象,根据此对象的类型,去catch中进行匹配
2.2.2 一旦try中的异常对象匹配到某一个catch时,就进入catch中进行异常的处理。一旦处理完成,就跳出当前的 try-catch结构(在没有写finally的情况)继续执行其后的代码
注意:在try结构中声明的变量,在出了try结构以后,就不能再被调用
解决:提前声明并初始化(局部)变量,在try-catch块中拿来用
常用的异常处理方式:
① String getMessage()
② printStackTrace()
2.3 catch中异常类型的顺序
有子父类关系:子类异常类型在上(有点像if-else嵌套循环范围小的在上)
没有子父类关系:先后顺序无所谓
2.4 finally
2.4.1 finally是可选的
2.4.2 finally作用
finally一定会被执行,即使:
catch又出现异常
try中有return
catch中有return
2.4.3 finally的使用场景
像数据库连接、输入输出流、网络编程Socket等资源,JVM是不能自动的回收的,我们需要自己手动的进行资源的释放,此时的资源释放,就需要声明在finally中
(四)方式2----throws + 异常类型
1. 过程(一直向上抛,直到try-catch处理)
"throws + 异常类型"写在方法的声明处。指明此方法执行时,可能会抛出的异常类型。
一旦当方法体执行时,出现异常,仍会在异常代码处生成一个异常类的对象,此对象满足throws后异常类型时,就会被抛出。
异常代码后续的代码,就不再执行
2. 和try-catch的区别
try-catch-finally:真正的将异常给处理掉了
throws的方式只是将异常抛给了方法的调用者。 并没有真正将异常处理掉
3. 开发中如何选择使用try-catch-finally 还是使用throws?
3.1 子父类关系角度
如果父类中被重写的方法没有throws方式处理异常,则子类重写的方法也不能使用throws(重写的规定之4),意味着如果子类重写的方法中有异常,必须使用try-catch-finally方式处理
子类抛出的异常必须小于等于父类throws的异常
3.2 多个方法和总方法的角度
执行的方法a中,先后又调用了另外的几个方法,这几个方法是递进关系执行的。我们建议这几个方法使用throws 的方式进行处理。而执行的方法a(大方法)可以考虑使用try-catch-finally方式进行处理。
四、手动抛出异常:throw
常见于if-else语句中
五、用户自定义异常类
步骤:
1. 继承于现有的继承结构
继承Exception:编译时异常
继承RunTimeException:运行时异常
2. 提供全局常量:序列版本号
3. 提供重载的构造器
(六)异常练习
练习一:
练习二
题目
自定义异常类
(七)总结
异常处理的五个关键字:
try catch finally
throws
throw
面试题:throw和throws的区别
配合结构(先手动抛throw,后throws抓)
throws:异常处理的一种方式(往上级报告,暂时不处理)
throw:异常产生阶段(手动生成异常对象,可以被throws“抓")
项目:开发人员调度软件
(一)注意:
基础问题;
1. Status枚举类的使用(全局常量的使用)
2. 导入了Data下的所有静态结构
可以省略Data
3. 二维数组的处理,下标获取哪个元素要清晰
4. 异常的处理
自定义异常类(继承;全局常量;构造器两个)
小函数的小功能就直接throws向上抛出,先不处理,以后统一try-catch处理
函数有扔异常,那么调用该函数的时候就要给出相应的处理异常的方法
一般自己throw出来的异常都要在函数声明的时候网上扔throws
测试的时候
提示:
解决:
5. 对象的思想
类型匹配问题(字符串和类的转换匹配)
输出问题:转换成字符串(重写toString()方法
调用父类的父类的toString()方法没法调:
解决:直接在父父类写一个getDetails()方法,避开toString()
6. 避开变量重复定义重名问题:声明在外,赋值在内
7. String类型和基本数据类型的转换(类型匹配问题)
8. 及时测试
写一部分就测试一下,因为main函数还没办法运行
9. 枚举类的赋值
static final类型的成员变量(是自己这个类的对象)
所以可以直接类名Status.成员名FREE调用
10.关于equals()
只要是引用的类型的比较值是否相等,就用equals()
为了避免空指针异常的风险,建议把字符串放在前面:
11. 多个if的先后顺序问题(可以筛选掉一部分)
12. return无论在方法的哪一个位置,都一定结束方法体(可以跟if结合起来看)
13. 尽量避免代码的冗余或者重复,尤其是涉及循环的
14. 关于判断是不是空
15. 重写toString()
逻辑问题
1. 添加员工条件判断&&和进了if再判断
正确写法:一步一步判断,有先后
错误:
2. 一个for循环的小问题
删除操作时没找到,则i是走到结束才退出循环的,不是break出的
3. 小简写
(二)步骤:
1. Domain类的JavaBean
Equipment接口及其实现类
Employee及其子类
2. Service类
3. View类
面向对象
0 条评论
回复 删除
下一页