你可能所不知道的细节
面向过程(执行者)
- 强调的是过程的实现。
面向对象(指挥官)
- 强调的是实现过程的对象。
- 面向对象两大概念:类和对象。
类和对象
类及其成员:
属性、方法、构造器、代码块、内部类。
- 设计类,就是设计类的成员。
-
属性 == 成员变量 == field == 域、字段
-
方法 == 成员方法 == 函数 == method
-
创建类的对象 == 类的实例化 == 实例化类
-
实例化:
- 类的使用
1.创建类的对象(new): 类名 对象名 = new 类名( )
a.每new一个对象,都会有一套独立体系(非static)
2.调用对象的结构 :
1)属性(有默认初始化值)——————> 对象名.属性
2)方法——————> 对象名.方法
属性、局部变量
- 属性定义在类的{ }内
- 局部变量可以在方法(形参、内部)、代码块、构造器(形参、内部)中
不同:
- 属性可加修饰符,局部变量不可。
- 属性根据类型,会有默认初始化值,局部变量需手动赋初始化值(形参会调用时传参赋值)。
- 在内存的加载位置不同:
- 属性:堆(非static)
- 局部变量:栈
方法
声明:
- 权限修饰符 返回值类型 方法名(形参列表) { 方法体 }
return的使用:
- 作用范围:方法体中。
- 作用:
- 结束方法
- 返回数据
- 注意:return后不可声明执行语句。
注意:
- 方法的使用中,可以调用当前类中的属性或方法。
- 一个方法中要调用另一个方法,需要先将类new出来。
- 方法中不可再定义方法。
匿名对象
- new对象后并没有将其赋值给一个变量去接收。
- 匿名变量只能使用一次(思考内存布局)。
匿名子类
正常情况是这样的:
①创建一个类: class Worker extends Person{ 重写Person方法 }
②new一个 类Worker 给一个 对象p: Person p =new Worker( )
(多态)
③通过 对象p 调用 类Worker 重写 类Person 的方法
现在情况是这样的:
Person p=new Person( ){ 重写Person方法 }
(多态)
- 因为没有也不能有其他的类名,因此用Person代替,后面跟了 { } 和方法体,这就是一个匿名子类
- 若将对象p也省略,即是匿名对象的匿名子类
- 若为匿名对象的匿名子类,想调用没有对象的子类的方法,就要在 { } 后通过《 . 方法( ) 》的方式来调用该方法
方法重载
两同一不同:
- 同一个类,相同方法名。
- 参数列表不同:个数、类型、顺序…
注意:
- 重载跟方法的权限修饰符、返回值类型、形参变量名、方法体无关。
可变参数
类型…形参变量名
- 可传递多个同类型的参数(0~n)。
- 必须和类型相符。
- 数组和可变参数可认为是一致的,因此不能构成重载。
(一个方法中只能有一个可变参数,且必须是传递的最后一个参数(位于最后))
值传递机制(思考内存布局)
参数是基本数据类型:形参的是实参的数据值。
参数是引用数据类型:形参的是实参存储数据的地址值。
封装
体现:
- 将类的属性或方法私有化(private),并提供公共(public)的方法来获取和设置该属性。
继承
class A extends B { }
A:子类、派生类、subclass
B:父类、超类、基类、superclass
- 为多态性的使用提供前提。
- 子类继承父类后,会获得所有的属性或方法(private也会继承,但受封装性影响,不能直接调用)。
- 一个子类只能有一个父类,一个父类允许有多个子类。
- 允许多层继承关系,因此子父类是相对概念。
- 如果没有显示的声明一个类的父类,则此类会继承java.lang.Object类。因此,所以的类直接或间接继承于Object类。
多态(向上转型)
- 一个事物的多种形态
对象的多态:父类的引用指向子类的对象(子类对象赋值给父类的引用)
使用:编译期只能调用父类中声明的方法,运行期执行的是子类重写父类的方法。
总结:
- 编译看左边,运行看右边。
- 需有类的继承关系,以及方法的重写。
- 多态仅适用于方法,不适用于属性(属性编译运行都看左边)。
- new对象时子类和父类的属性和方法都会加载到堆中,只是编译时只看左边。
instanceof(向下转型)
多态性中new对象时子类和父类的属性和方法都会加载到内存中,但编译时只看左边,想要加载右边也有的结构,就需要向下类型转换(高类型转低类型),而强转有可能出现 ClassCastException 异常,因此用到 instanceof 关键字。
使用: a(对象) instanceof A(类)
- 判断a是不是由A类或者是A类的子类所实例化的对象,如果是,则返回true
- 判断a是不是非A类或非A类的子类所实例化的对象,如果是,再判断是否与A类有继承树关系,如果有,则返回false,无任何关系,则编译器报错
方法的重写
- override/overwrite
方法的声明:
权限修饰符 返回值类型 方法名( 形参列表 ) throws 异常类型 { 方法体 }
- 子类继承父类后,可对父类同名同参数的方法进行覆盖操作。
作用:当子类调用父类同名同参数的方法时,将执行子类重写父类后的方法。
- 父类调用自身方法与重写无关(new的对象不同)
注意:
-
子类重写的方法和父类被重写的方法的方法名和形参列表必须相同。
-
子类重写的方法的权限修饰符不小于父类被重写的方法的修饰符
- 但当父类的方法被private修饰时,子类不会对其重写,会是个新的方法。
-
返回值类型:> void:必须相同。
> 引用类型:被重写的方法是A类时,重写的方法可以是A类 或A类的子类。
> 基本类型:必须相同。
-
子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型
-
子类和父类同名同参数的方法要么都声明为static(可以考虑重写),要么都为非static(已非重写)。
总结
属性赋值先后顺序:
- 默认初始化
- 显示初始化、代码块(按声明的先后顺序赋值)
- 构造器中初始化
- 通过对象赋值
权限修饰符
使用范围:
- private——————>类内部
- (缺省)——————>同一个包
- protected——————>不同包的子类(extends继承)
- public——————>同一个工程(不同包下需导包)
注意:
- 4种权限都可以用来修饰类的内部结构(方法、属性、构造器、内部类)
- 外部类只能使用:缺省、public
内存解析
1.栈:
- 存放局部变量
2.堆:
- 存放new出来的结构:对象、数组
- 每次new出来的结构都会在堆中重新开辟块空间
- 当栈内无任何变量指向堆的结构,则该结构的空间会在之后的某个时间点被回收
3.方法区:
- 分为两个区:常量池、静态域
注意:
- 引用变量只可能存放两种:null或地址值。
构造器
格式:
权限修饰符 类名 ( 形参列表 ) { }
- 造对象时,new后面的其实就是构造器
- 如果没有显示的定义构造器,系统会默认带一个空参构造器(该构造器权限和类的权限相同)
- 构造器中存在重载
- 若手动创建了构造器,系统就不再提供默认的空参构造器了
- 构造器可以调用方法
- 类一定会有构造器
作用:
- 创建对象
- 初始化对象信息
两大关键字
this关键字
this可以用来修饰:属性、方法、构造器
- this应理解为:当前对象或当前正在创建的对象。
- 当形参和属性重名等情况,就可以考虑用this来指明该属性。
this调用构造器:
- 在类的构造器中,可以使用this(形参名)来调用本类中其他的构造器。
- 构造器不能自己调用自己,且一个类中最多只有n-1个,而最后那一个一定是调用了super( ) 。
- this(形参名)只能声明在构造器中的首行。
super关键字
-
- 可以理解为:父类的…
super可以用调用:属性、方法、构造器
-
当子类和父类中定义了同名的属性时,若想在子类中调用父类声明的属性,就必须用 super.属性 的方式,表明调用的是父类中声明的属性。
-
当子类重写了父类的方法时,若想在子类中调用父类被重写的方法,则必须使用 super.方法 的方式,表明调用的是父类被重写的方法。
super调用构造器:
- 在子类构造器中可以使用super ( 形参列表 )的方式调用父类声明的构造器
- super( 形参列表 )必须声明在子类构造器的首行,因此this( 形参列表 )和super( 形参列表 )不能同时出现
- 若在构造器首行没有使用this( 形参列表 )或super( 形参列表 ),则会默认调用父类的空参构造器 super( )
- 一个子类的多个构造器中至少有一个构造器调用了父类的构造器(不是super( 形参列表 ) 就是 super( ) )
- 父类和子类是相对概念,因此最后的子类一定会间接的调用了Object类的空参构造器
package、import
package:
- package声明在源文件首行
- 包属于标识符,要遵守命名规范
- 每 . 一次,就代表一层文件目录
- 同个包下不可有同名的类或接口
import:
- 用于导入指定包下的类或接口
- 声明在包和类的声明之间
- 导入多个可使用 * 代替(*不会导入子包)
- java.lang包下的类或接口是默认导入的(lang包下的子包不会默认导入)
- 本包下的类或接口可以省略import结构
- 如果在源文件中使用了不同包下同名的类,则必须其中一个类以全类名的方式显示(即路径必须表达完整)
- import static:导入指定类或接口中的静态结构(属性或方法)
Object类
- Objiect类是所有Java类的根父类。
1.如果类中为使用extends关键字指明其父类,则会默认继承Object类。
2.Object类的属性,方法具有通用性:
-
属性:无
-
方法:equals( )/toString( )/getClass( )/hashCode( )/clone( )/finalize( )/
wait( )、notify( )、notifyAll( )
-
构造器:空参
==和equals( )的区别
- ==:运算符
1.可以使用在基本数据类型和引用数据类型中。
2.-> 基本数据类型:比较两个变量保存的数据是否相等(类型可以不一样)
-
- boolean不参与与其他类型的比较
-> 引用数据类型:比较两个对象的地址值是否相同。
3.使用时必须确保左右两边的变量类型一致(基本、引用)。
- equals( ):方法
1.只能用于引用数据类型。
2.Object类中定义的equals( )和==的作用是一样的:比较两个对象的地址是否相同
重写:
public boolean equals(Object obj){ }
3.String、Date、File、包装类等重写了Object类的equals( )方法,比较的是两个对象的“实体内容”是否相同。
toString( )
-
当输出一个对象的引用时,实际就是调用当前对象的toString( )。
-
当输出对象的引用值为null时,不会调用toString( )。
重写:
public String toString(){ }
-
像String、Date、File、包装类等都重写了Object类中的toString( ),返回调用对象的实体内容。
包装类
----->使基本数据类型的变量有了类的特征。
继承父类Number:
-
byte–>Byte short–>Short int–>Integer long–>Long
float–>Float double–>Double
boolean–>Boolean char–>Char
1.基本数据类型–>包装类(自动装箱):创建包装类对象时调用构造器
包装类型 变量名 = 基本类型变量
2.包装类–>基本数据类型(自动拆箱):包装类的对象变量名 . 要转类型Value( )
基本类型 变量名 = 包装类型变量
3.基本数据类型、包装类–>String类型:
1)连接运算
2)调用String . valueOf( 对应类型变量 )
4.String类型–>基本数据类型、包装类:包装类型 . parse基本类型( String 变量 )
注意:
- 除了boolean,其他类型在不规范转换时,有可能出现NumberFormatException异常。
static关键字
----->用来修饰:属性、方法、代码块、内部类
1.static修饰属性:静态变量
1)非静态变量(实例变量):
当创建多个类的对象时,每个对象都拥有自己的非静态属性(实例变量),当修改其中一个对象的非静态变量,不会影响其他对象的同名非静态属性。
2)静态变量(类变量):
当创建类的多个对象时,多个对象共享同一个静态变量,当通过某个对象修改静态变量时,会导致其他对象的同名静态属性也发生修改。
注意:
- 静态变量随着类的加载而加载,因此可以通过 类 . 静态变量 的方式调用。
- 类加载后才能造对象,因此静态变量的加载要早于对象的创建。
- 类只会加载一次,因此静态变量在内存中也只会存在一份:位于方法区的静态域中。
- 当属性是被多个对象共享的,不会随着对象的不同而变化时,可以考虑使用static修饰属性
2.static修饰方法:静态方法
1)随着类的加载而加载,可通过 类 . 静态方法 的方式调用。
2)静态方法中只能调用静态的方法和属性,非静态方法既可以调用非静态的方法和属性,也可以调用静态的方法和属性。
注意:
- 静态方法内,不能使用this和super关键字。
- 静态属性和方法的调用应该从生命周期角度思考。
- 操作静态属性的方法和工具类,一般都为static修饰
代码块 { }
- 修饰符只能有static
- 用来初始化类、对象
静态代码块:
- 内部可以有输出语句
- 随着类的加载而加载,且只执行一次
- 可以有多个静态代码块,按照声明的先后顺序执行
- 只能调用静态结构,不能调用非静态结构
非静态代码块:
- 内部可以有输出语句
- 随着对象的创建而执行
- 每创建一个对象,执行一次非静态代码块
- 也按照声明的先后顺序执行
- 可以调用静态结构,也可以调用非静态结构
- 由父及子,静态先行
final关键字
- 意思是:最终的
- 用来修饰:类、方法、变量
1)用来修饰类:此类不能被其他类继承。
- String类、System类、StringBuffer类
2)用来修饰方法:此方法不能被重写。
- Object类中的getClass( )
3)用来修饰变量:此时的"变量"被称为常量。
-
final修饰属性:
可以赋值的地方:显示初始化、代码块中初始化、构造器中初始化。
-
final修饰局部变量:
表示一个常量,且在修饰形参时也是一个常量,当给此方法传递了参数后,该形参值在该方法体内不能被重新赋值。
abstract关键字
- 可以用来修饰类、方法
修饰类:抽象类
- 此类不能实例化,但类中一定有构造器(让子类调用)
- 一般都会通过子类实现相关操作
修饰方法:抽象方法
- 抽象方法只有方法的声明,没有方法体
- 为了不让类调用抽象方法,因此类也必须抽象
- 为了不让子类调用抽象方法,子类必须重写父类(直接或间接)的所有抽象方法或让子类抽象化
接口
- 使用interface定义
JDK7及以前:只能定义全局常量和抽象方法
-
全局常量:public static final(可省略不写,默认)
抽象方法:public abstract(可省略不写,默认)
JDK8:除了全局常量和抽象方法,还可以定义静态方法、默认方法
- 接口中定义的静态方法,只能通过接口来调用
- public default:默认方法,public可省略
- 通过类的对象可以调用接口中的默认方法
- 若类重写了接口中的默认方法,则调用的是重写后的方法
- 若子类继承的父类和实现的接口中声明了同名同参数的默认方法,则子类会在没有进行重写的情况下,默认调用父类的方法—>类优先原则
- 如果类要实现多个接口,而这些接口里面拥有同名同参数的默认方法,若类没有重写该方法,则会报错。
- 通过 接口名 . super . 默认方法名 来调用重写前的接口的默认方法
注意:
- 接口不能定义构造器:接口不可实例化
- 类通过implements去实现接口
- 若类未实现接口中的所有抽象方法,则此类依旧为一个抽象类,不可实例化
- 可以实现多个接口
- 接口与接口之间可以多继承
内部类
1.成员内部类(静态、非静态)
-
作为外部类的成员:
1)可以调用外部类能调用的结构
2)可以被4种权限修饰符修饰
。。。。。。
-
作为一个类:
该有的都能有。。。
2.局部内部类
- 在局部内部类的方法里想调用局部内部类所在的方法里的局部变量,此变量被声明为final
注意:
-
实现内部类对象:
静态:
- 外部类名 . 内部类名 变量名=new 外部类名 . 内部类名( )
非静态:(先造外部对象)
- 外部类名 . 内部类名 变量名 =外部对象变量名 . new 内部类名( )
异常
- 抓抛模型
一、抛:
程序在正常执行过程中,一旦出现异常,就会在异常代码处生成一个对应异常类对象,并将此对象抛出,后续代码将停止执行。
- ①系统自动生成异常对象
- ②手动new一个异常对象,并抛出(throw)
二、抓:
异常的处理方式:① try-catch-finally ② throws
- try-catch-finally:
try{可能出现异常的代码}
catch(异常类型1 变量名1){处理异常方式1}...
finally{一定会执行的代码}
注意:
-
finally可选
-
try中代码抛出异常后将直接进去catch中进行异常匹配处理,处理后将继续执行后续代码
-
catch中的异常类型若有子父类关系,则子类要求声明在父类上面
-
常用的处理异常方式:①(返回值是String)getMessage( )
②(返回值是void)printStackTrace( )
-
try-catch-finally只是处理编译时异常,但运行时仍可能报错
-
try-catch-finally结构可以嵌套
-
finally里的代码是一定会执行的,即使是catch又出现异常、try/catch中有return语句(如果finally也有return,则返回finally的return)
- throws + 异常类型
注意:
- 写在方法的声明处,指明执行此方法时可能抛出的异常
- 执行方法体时出现异常,则会在异常代码处生成一个异常类对象,满足throws后的异常类型时,就会被抛出,后续的代码不会被执行
自定义异常类:
- 继承于现有的异常类型
- 提供一个全局常量:serialVersionUID(序列号)
- 提供重载的构造器
- 利用super( )来调用父类打印错误信息
此文章废话连篇,但如果对您有帮助,请点个赞吧(新手发文,大佬勿辱,若有不对之处,请留言指明,大家一起进步。谢谢谅解)。