这里写自定义目录标题
面向对象编程
类与对象
面向对象的理解
-
面向对象和面向过程的区别
-
都是编程思想
-
面向过程强调的是 “怎么做”
- 面向过程的程序,是以函数为基本单位
-
面向对象强调的是“谁来做”
- 面向对象是关注的是对象的个体,以类为基本单位,重点在于类的设计,某个行为,数据是在哪个类中描述更合适
-
-
面向对象的好处
- 解决中大型的问题,使用面向对象 代码简化了
- 以人类的思维方式来思考问题,比较好理解
-
面向对象的思考步骤
- 1、先分析里面涉及到几个类,分别是什么
- 2、抽取每个类中的属性和方法,再加以定义
- 3、面向过程正常调用和执行
类与对象
-
什么是类?
- 对现实世界中,具有共同特性的某一类事物的抽象的描述,在Java中使用一个类来描述一类事物
-
什么是对象?
- 对象是类的一个具体的个体,是类的一个实例(instance)
-
类是对象的设计模板,对象是类的实例
-
二者的区别
- 类是抽象的
- 对象是具体的,实际存在的,也称为 实体或实例
-
二者的关系
- 类是由对象总结或抽取出来的一个概念。 是对象的所属类型
- 对象是通过类创建出来的具体的实体
如何定义一个类?
-
语法格式
- 【修饰符】 class 类名{
属性列表
构造器列表
方法列表
}
- 【修饰符】 class 类名{
对象的内存结构图
-
栈
- 存放方法中的基本类型变量
- 存放方法中的引用类型 变量的引用(地址号)
-
堆
- 存放对象本身
-
方法区
- 存放类的信息:类名、包名、属性的描述信息、方法的描述信息
类的内部成员
属性
-
变量的分类:
-
成员变量:
-
实例变量
-
先声明,后使用;
-
实例变量有其所在的一个作用域{},只有在其定义的作用域中有效;
-
随着对象的创建而产生,分配在堆空间;
-
可以采用默认初始化,也可以采用其他方式进行初始化;
-
每个对象均拥有一份实例变量,通过对象.属性修改此实例变量;
-
可以声明权限修饰符;修饰符有:public 、缺省 、protected、private ;
- private类内部;缺省同一个包;protect不同包子类;public同一个工程
- class的只能用public任意地方访问;default 同一个包内部的类访问
-
-
类变量
- 采用static关键词修饰;
- 采用类名 . 类变量的方法调用;
- 随着类的加载而产生,分配在方法区,类变量早于方法的创建;
- 类变量为类的所有对象共享,一经改变,后续调用为改变后的值;
-
-
局部变量:
- 局部变量声明在:方法体中、方法的形参、内部类、代码块、构造器(构造器的形参);
- 局部变量也需要先声明赋值,后使用,也可采用默认的初始化值。注意:方法的形参为特殊,它的初始化为方法调用时传入的值;
- 局部变量没有权限修饰符,它的权限跟随方法,或者构造器的权限;
- 局部变量存放在栈空间;
-
-
如何声明
-
声明格式
-
【修饰符】 数据类型 属性名;
-
如何在声明类时没有显式(手动)给属性赋初始值,那么在创建对象后,这个属性有默认的初始值
- byte,short,int,long 初始值为0
- float,double 初始值为0.0
- boolean 初始值为false
- char 初始值为\u0000
- 引用数据类型(类、接口、数组) 初始值为null
-
-
【修饰符】 数据类型 属性名 = 初始值;//显式初始化
-
-
声明在类中,方法外
-
-
封装
-
在属性的修饰符位置,使用private修饰
- 只能在本类中直接使用,在其他类中不可见
-
提供公共的get/set
public class Student{
private String name;
private int age;public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
public void setAge(int age){
this.age = age;
}
public int getAge(){
return age;
}
}-
set方法
- 用于为属性赋值,修改属性值
-
get方法
- 用于获取,访问某个属性的值
-
-
-
对属性的赋值操作:
-
① 默认初始化 --> ②显式初始化 / ③ 代码块初始化–>④ 构造器初始化 -->⑤ 变量名. 方法 或者 变量名. 属性
默认初始化的初始化值:
整型变量:byte、short、int、long ---->0
浮 点 型:float、double ---->0.0
布 尔 型:boolean ---->false
字 符 型:char ---->0 或/u0000
引用类型:接口,数组、类 ---->null
-
方法
-
基本概念
-
方法:代表一个功能的定义,具有可重用性,就可以把这样的一段代码,抽象成一个方法
-
一个方法完整的定义:
-
方法的签名
- 修饰符 返回值类型 方法名(形参列表)抛出的异常列表
-
方法的实现
- {
方法的实现
}
- {
-
-
结论:
-
1、只要是无返回值的方法的调用,只能是单独一个语句
-
2、在其他类中
-
如果被调用的方法是static修饰
-
被调用的方法是无参无返回值的
- 类名.方法名();
-
被调用的方法是有参无返回值的
- 类名.方法名(实参列表);
-
被调用的方法是无参有返回值
- 变量名 = 类名.方法名();
-
被调用的方法是有参有返回值
- 变量名 = 类名.方法名(实参列表);
-
-
如果被调用的方法是非static修饰的
- 同上,就是“类名.”改成“对象名.”
-
-
3、要不要传实参,看被调用的方法是否声明了形参
-
4、要不要接收返回值,看被调用的方法返回值类型是不是void,如果不是void,就可以接收,如果是void,就不能接收
-
-
-
方法的分类
-
四种形式
-
无参无返回值
-
声明格式
- 修饰符 void 方法名(){
方法体
}
- 修饰符 void 方法名(){
-
调用格式
-
(1)在本类中
-
方法名();
- 单独一个语句
-
-
(2)在其他类中
-
如果被调用的方法是static修饰
- 类名.方法名();
-
如果被调用的方法是非static修饰的
-
对象名.方法名();
- Scanner input = new Scanner(System.in); //声明了Scanner对象
- input.nextInt();
-
-
-
-
-
有参无返回值
-
声明格式
-
修饰符 void 方法名(形参列表){
方法体
}-
其中形参列表
-
形参的数据类型 形参名1, 形参的数据类型 形参名2
- 每一个形参的声明之间使用,分割
-
-
-
-
调用格式
-
(1)在本类中
-
方法名(实参列表);
-
单独一个语句
-
实参列表
- 实参直接是“常量值”,“变量名”,“表达式”
- 实参的个数、顺序、数据类型必须与被调用的方法的形参的个数、顺序、数据类型一一对应
-
-
-
(2)在其他类中,加上类名/对象名.即可
-
-
-
无参有返回值
-
声明格式
- 修饰符 返回值类型 方法名(){
方法体
}
- 修饰符 返回值类型 方法名(){
-
调用格式
-
(1)在本类中
-
(A)经典形式
-
变量名 = 方法名();
- 注意:这个变量的数据类型必须与被调用方法的返回值类型一致(或兼容)
-
-
(B)作为另一个方法调用的实参
class TestReview{
public static int getVolume(int area, int height){
return area * height;
}
public static double getArea(double lenght, double width){
return lenght * width;
}
public static void main(String[] args){
int a = 2;//长
int b = 3;//宽
int h = 4;//高
double area = getArea(a,b);//把方法的返回值赋值给一个变量
//变量area的类型要与 方法getArea()的返回值类型 一致
//int v = getVolume(getArea(a,b) ,h);//错误
//getArea(a,b)的返回值类型是double,而getVolume()的第一个形参是int
double v = getArea(a,b) * h; //把方法的返回值 作为表达式的一部分
}
}-
System.out.println(方法名());
- 被调用的方法的返回值类型与另一个方法的形参的类型必须一致或者兼容
-
getVolume( getArea() , height);
- getArea()的返回值类型与getVolume()的第一个形参的类型要求一致
-
-
(C )作为表达式的一部分
class TestReview{
public static int getVolume(int area, int height){
return area * height;
}
public static double getArea(double lenght, double width){
return lenght * width;
}
public static void main(String[] args){
int a = 2;//长
int b = 3;//宽
int h = 4;//高
double area = getArea(a,b);//把方法的返回值赋值给一个变量
//变量area的类型要与 方法getArea()的返回值类型 一致
//int v = getVolume(getArea(a,b) ,h);//错误
//getArea(a,b)的返回值类型是double,而getVolume()的第一个形参是int
double v = getArea(a,b) * h; //把方法的返回值 作为表达式的一部分
}
}
-
-
-
-
有参有返回值
-
声明格式
- 修饰符 返回值类型 方法名(形参列表){
方法体
}
- 修饰符 返回值类型 方法名(形参列表){
-
调用格式
-
(1)在本类中
-
(A)经典形式
-
变量名 = 方法名(实参列表);
- 注意:这个变量的数据类型必须与被调用方法的返回值类型一致(或兼容)
-
-
(B)作为另一个方法调用的实参
class TestReview{
public static int getVolume(int area, int height){
return area * height;
}
public static double getArea(double lenght, double width){
return lenght * width;
}
public static void main(String[] args){
int a = 2;//长
int b = 3;//宽
int h = 4;//高
double area = getArea(a,b);//把方法的返回值赋值给一个变量
//变量area的类型要与 方法getArea()的返回值类型 一致
//int v = getVolume(getArea(a,b) ,h);//错误
//getArea(a,b)的返回值类型是double,而getVolume()的第一个形参是int
double v = getArea(a,b) * h; //把方法的返回值 作为表达式的一部分
}
}-
System.out.println(方法名(实参列表));
- 被调用的方法的返回值类型与另一个方法的形参的类型必须一致或者兼容
-
getVolume( getArea(实参列表) , height);
- getArea()的返回值类型与getVolume()的第一个形参的类型要求一致
-
-
(C )作为表达式的一部分
class TestReview{
public static int getVolume(int area, int height){
return area * height;
}
public static double getArea(double lenght, double width){
return lenght * width;
}
public static void main(String[] args){
int a = 2;//长
int b = 3;//宽
int h = 4;//高
double area = getArea(a,b);//把方法的返回值赋值给一个变量
//变量area的类型要与 方法getArea()的返回值类型 一致
//int v = getVolume(getArea(a,b) ,h);//错误
//getArea(a,b)的返回值类型是double,而getVolume()的第一个形参是int
double v = getArea(a,b) * h; //把方法的返回值 作为表达式的一部分
}
}
-
-
-
-
-
-
命令行参数(了解)
-
给main方法传的参数
-
public static void main(String[] args)
- 通过命令行传的参数,在args数组中
- 如果要使用命令行的参数 args[0]。。。
-
怎么传
-
java 包.类 参数1 参数2 参数3。。
- 参数之间使用空格
-
eclipse
-
run菜单–>run configration–>选择运行哪个main方法 --》 program argument
- 各个参数空格分隔
-
-
-
-
可变参数
-
可变参数的声明
-
数据类型… 形参名
- 声明必须在方法的形参列表的最后一个
- 一个方法只能有一个可变参数
- 可变参数的数据类型 可以 是任意类型
-
-
可变参数的使用
- 在声明它的方法中,可变参数当做数组使用
- 形参名就是数组名
-
可变参数的方法调用时
- 1、可变参数的对应的位置,可以传0~n个实参
- 2、可变参数的对应的位置,可以传1个数组对象,当然这个数组对象的元素数据类型必须与可变参数的数据类型是一致
-
示例
找多个整数中的最大值,至少一个
int getMax(int a , int… b){
int max= a;
for(int num : b){
if(max<num){
max=num;
}
}
return max;
}
-
-
方法重载
-
特点:
- 同一个类中,方法名相同,参数列表不同(参数个数或参数类型或参数顺序)不同,返回类型不要求
-
作用:
- 允许功能相似的方法重名,使方法定义和调用都简化了
-
调用:
- 正常方法调用即可,传进对应的参数,编译器自动选择 你指定类型参数的方法执行
-
-
方法的重写
-
重写的定义:
- 在子类中可以根据需要对从父类中继承来的方法进行改造,也称为方法的重置、覆盖。在程序执行时,子类的方法将覆盖父类的方法;
-
重写的要求:
- ① 重写方法必须和被重写方法具有相同的方法名称、参数列表;
- ② 重写的方法的返回值类型不能大于被重写的方法的返回值类型;
- ③ 重写方法使用的访问权限不能小于被重写方法的访问权限;
- ④ 子类方法抛出的异常不能大于父类被重写方法的异常;
- 注意:① 子父类中同名的方法(非重写)须同时为 static 的,或同时为非 static 的;② 子类不能重写父类中声明为 private 权限的方法;
-
-
方法参数传递
-
java中是按值传递的
-
基本类型做参数
- 值代表的是数值内容,传参相当于拷贝了一个备份到形参。形参的改变不影响实参
-
引用类型做参数
- 值代表的是地址号,传参相当于传递了一个地址号(引用)到形参。形参的改变影响实参
-
-
实参将值传给形参的过程,称为参数传递,实际上就是一个赋值的过程
-
-
方法的递归:
- ① 方法递归包含了一种隐式的循环,它会重复执行某段代码,但这种重复执行无须循环控制;
- ② 递归一定要向已知方向递归,否则这种递归就变成了无穷递归,类似于死循环;
构造器
-
又称为构造方法
-
如何声明
-
(1)无参构造
- 【修饰符】 构造器名(){
方法体
}
- 【修饰符】 构造器名(){
-
构造器都会默认的声明super关键字,调用父类的构造器,不显示的声明,则程序会自动的调用Object类的无参构造器;
-
-
特点
-
(2)有参构造
- 【修饰符】 构造器方法名(形参列表){
方法体
}
- 【修饰符】 构造器方法名(形参列表){
-
A:构造器方法名必须与类名相同
B:构造器没有返回值类型
C:构造器可以重载
D:任何类中都有构造器,如果没有显式/手动声明构造器,编译器将自动添加一个默认的无参构造器
E:如果显式/手动声明了任何一个构造器,编译器将不再自动添加默认的无参构造
-
-
作用
-
(1)和new一起使用,创建对象
-
new 构造方法()
-
调用无参构造
- Random rand = new Random();
-
-
new 构造方法(实参列表)
-
调用有参构造
- Scanner input = new Scanner(System.in);
-
-
-
(2)初始化对象的属性
-
-
注意:抽象类中也有构造器,但是,抽象类中的构造器不能实例化,是为了让子类继承时调用;
代码块
-
静态代码块
-
声明格式
- static{
}
- static{
-
作用
- 为静态属性赋值
-
-
非静态代码块
-
声明格式
-
{
}- 在类中,称为构造块
- 在方法中,称为普通代码块,这里不做说明
-
-
作用
- 为非静态属性赋值
- 当多个构造器中重复的代码,可以提取到构造块中
-
-
执行的特点
- 静态代码块(只执行一次)–》构造块(每次创建对象) --》构造器(每次创建对象)
-
示例
package com.atguigu.review;
public class Son extends Father {
public Son() {
System.out.println(“4444444444”);
}
{
System.out.println(“555555555555”);
}
static {
System.out.println(“66666666666”);
}
public static void main(String[] args) {
System.out.println(“77777777777777”);
new Son();//3 6 7 2 1 5 4
System.out.println("!!!");
new Son();//2 1 5 4
System.out.println("!!!");
new Father();//2 1
}
}
class Father {
{
System.out.println(“22222222222”);
}
public Father() {
System.out.println(“11111111111”);
}
static {
System.out.println(“3333333333”);
}
} -
代码块的分类:
-
静态代码块:
- ① 随着对象的创建而执行,可以提供输出语句。
- ② 作用:初始化类的加载信息;
- ③ 内部只能调用当前类中静态的属性、方法。不能调用非静态的属性、方法;
- ④ 如果定义了多个静态代码块,照定义的先后顺序执行;
- ⑤ 静态代码块的执行要早于非静态代码块;
- ⑥ 随着类的加载而执行,且只被加载一次;
-
非静态代码块:
- ① 随着对象的创建而执行;
- ② 每创建一个对象,都会执行一次;
- ③ 作用:用来初始化对象的属性信息;
- ④ 可以调用当前类的方法和属性;
-
相同点:
- 均可以有输出语句;
- 如果定义了多个静态或者非静态代码块,照定义的先后顺序执行;
-
内部类
-
根据声明的位置不同
-
成员内部类
-
根据是否有static修饰
- 静态成员内部类,简称静态内部类
- 非静态成员内部类,简称成员内部类
-
-
局部内部类
-
根据是否有名字
- 局部内部类
- 匿名局部内部类,简称匿名内部类
-
-
-
各种内部类的特点
-
成员内部类
-
如何声明
- 【修饰符】 class 外部类{
【修饰符】 class 内部类{
}
}
- 【修饰符】 class 外部类{
-
如何使用
-
(1)在外部类中使用内部类
- 除了在外部类的静态成员中,不能直接使用内部类
- 在外部类的非静态成员部分,把内部类当做普通类使用
-
(2)在外部类的外面使用内部类
-
它需要外部类的对象,才能获得内部类的对象等
-
示例一
class Outer{
class Inner{
}
}
//使用
Outer.Inner inner = new Outer().new Inner();
Outer out = new Outer();
Outer.Inner inner = out.new Inner(); -
示例二
class Outer{
class Inner{ } public Inner getInner(){ return new Inner(); }
}
//使用
Outer out = new Outer();
Outer.Inner in = out.getInner();
-
示例三
class Outer{
class Inner{
}
}
//继承 Inner
MyClass需要调用Inner的构造器
而调用Inner的构造器,需要外部类Outer的对象
class MyClass extends Outer.Inner{
public MyClass(Outer out){ out.super(); }
}
-
-
-
(3)在内部类中使用外部类的成员
- 没有限制,都能用,包括静态的,包括私有的
-
-
其他特点
-
(1)外部类的权限修饰符只能是public 和缺省的,而成员内部类的权限修饰符可以4种
-
(2)非静态的成员内部类中是不能包含静态成员的(静态的属性,静态方法,静态的代码块)
-
(3)成员内部类有自己独立的字节码文件: 外部类名$内部类名.class
- 在类型描述上使用:外部类名.内部类名
-
-
-
静态内部类
-
如何声明
-
【修饰符】 class 外部类{
【修饰符】static class 内部类{
}
}- (1)需要在内部类中声明静态成员,那么这个内部类必须是static
- (2)需要在外部类的静态成员中使用这个内部类,那么这个内部类也必须是static
-
-
如何使用
-
(1)在外部类中使用
- 没有限制
-
(2)在外部类的外面
-
示例一
class Outer{
static class Inner{
public void test(){ }
}
}
//如何调用test()
(1)从test()看,它不是静态的,因此需要Inner的对象
(2)Inner在Outer里面,而Inner又是static修饰
Outer.Inner in = new Outer.Inner();
in.test();
-
示例二
class Outer{
static class Inner{ public static void test(){ } }
}
//使用test()
(1)看test(),是static修饰符,说明不需要Inner对象
(2)看Inner是static修饰,也不需要外部类的对象
Outer.Inner.test();
-
-
(3)在内部类中使用外部类的成员
- 在静态内部类中,不能使用外部类的非静态成员
- 其他的成员可以直接使用,包括私有的
-
-
其他的特点
- (1)静态的内部类,可以使用权限修饰符 4种
- (2)静态的内部类可以包含静态的成员
- (3)静态的内部类也有自己的独立的字节码文件:外部类$内部类.class
-
-
局部内部类
-
如何声明
- 【修饰符】 class 外部类{
【修饰符】 返回值类型 方法名(形参列表){
class 内部类名{
}
}
}
- 【修饰符】 class 外部类{
-
如何使用
-
(1)在外部类中
- 只能在声明它的方法中,而且作用域只在声明处开始,到它所属的}结束
-
(2)在外部类的外面
-
不能直接使用局部内部类的类型
-
但是,在外部类的外面,是否可以得到局部内部类的对象
package com.atguigu.review;
public class TestLocal {
public static void main(String[] args) {
MyInter m = new Out().test1(); Object obj = new Out().test2();
}
}
interface MyInter{
}
class Out{
//使用父接口类型返回
public MyInter test1(){
class Inner implements MyInter{ } return new Inner();
}
//使用父类类型返回
public Object test2(){
class Inner implements MyInter{ } return new Inner();
}
}
- 可以
-
-
(3)在内部类中使用外部类的成员(成员变量,成员方法)
- 它是否可以使用某个外部类的成员,受所在方法的影响(static)
-
(4)在内部类中使用外部类的局部变量
- 只能使用声明该内部类方法中,并在内部类之前声明的局部变量
- 而且这个局部变量要加final修饰
-
-
其他的特点
- (1)不能使用public,protected,private,static这些成员修饰符
- (2)局部内部类中,也不能包含静态成员的
- (3)局部内部类也有自己的字节码文件:外部类$编号内部类.class
-
-
匿名内部类
-
如何声明
-
【修饰符】 class 外部类{
【修饰符】 返回值类型 方法名(形参列表){
new 父类/父接口(){
//内部类的成员
}
}
}- 此处,父类要注意是否有无参构造
-
-
如何使用
-
(1)使用父类的多态引用使用它对象
-
只能调用父类中声明过的方法
package com.atguigu.review;
//在main中,写一个Father的匿名内部类的对象
public class TestNiMing {
public static void main(String[] args) {
Father f = new Father("尚硅谷"){ /*@Override public String getInfo() { return "内部类的示例:" + getInfo();//java.lang.StackOverflowError }*/ @Override public String getInfo() { return "内部类的示例:" + super.getInfo(); } public void method(){ System.out.println("匿名内部类自己的方法"); } }; System.out.println(f.getInfo());
// f.method();//无法访问
}
}
class Father{
private String info;
public Father(String info) {
super(); this.info = info;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
}
-
-
(2)本态引用调用方法
-
可以调用父类的方法,也可以调用子类自己的方法
public static void main(String[] args) {
//父类是Object,使用匿名内部类,声明一个方法void fun(),并调用 new Object(){ public void fun(){ System.out.println("匿名内部类的方法"); } }.fun();
}
-
-
(3)作为方法的实参
package com.atguigu.review;
import java.util.Arrays;
import java.util.Comparator;
public class TestLocal3 {
public static void main(String[] args) {
Dog[] dogs = new Dog[3]; dogs[0] = new Dog(7.8); dogs[1] = new Dog(5.8); dogs[2] = new Dog(9.8); Arrays.sort(dogs, new Comparator(){ @Override public int compare(Object o1, Object o2) { Dog d1 = (Dog) o1; Dog d2 = (Dog) o2; if(d1.getWeight() > d2.getWeight()){ return 1; }else if(d1.getWeight() < d2.getWeight()){ return -1; } return 0; } });
}
}
class Dog{
private double weight;
public Dog(double weight) {
super(); this.weight = weight;
}
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
}
-
-
其他特点
- (1)没有修饰符
- (2)没有构造器
- (3)没有类名
- (4)只有一个对象,声明匿名内部类与创建对象,同时进行
- (5)也有自己独立的字节码文件:外部类名$编号.class
-
-
-
为什么使用内部类
- 1、在一个类还有一个完整的结构(有属性,方法等),需要使用内部类,而且这个内部类只为外部类服务
- 2、内部类可以使用外部类的成员,包括私有的
设计模式
单例设计模式
-
特征
- 某个类型在整个系统中,只有一个唯一的对象
-
形式
-
1、懒汉式
- class Single{
private Single(){};private static Single instance = null;
- class Single{
public static Single getInstance(){
if(instance == null){
instance = new Single();
}
return instance;
}
}-
2、饿汉式
- class Single{
public static final Single INSTANCE = new Single();
private Single(){}
} - class Single{
private Single(){}; private static Single instance = new Single();
- class Single{
public static Single getInstance(){
return instance;
}
}- 3、枚举式
-
-
要点
- (1)构造器私有化
- (2)在单例类中创建这个唯一的对象
模板设计模式
-
特征
- 在父类中能确定某个功能的整体的算法结构,但是对于其中的某个步骤,在父类中无法确定,要延迟到子类中去实现,这个时候就可以通过模板设计模式
-
属于抽象类的应用
-
示例
计算任意一段代码或功能的运行时间
//父类:确定算法结构
abstract class SuperClass{
public final long getTime(){
//1、获取开始时间
long start = System.currentTimeMillis();//2、运行要计算时间的代码 run(); //3、获取结束时间 long end = System.currentTimeMillis(); //4、计算 return end - start;
}
protected abstract void run();
}
class SubClass extends SuperClass{
protected void run(){
//要测试时间的代码
}
}
工厂设计模式
-
目的:使得使用者与创建者分离
- 把创建某些接口的实现类对象交给工厂来做
-
简单工厂模式
-
示例
//1、所有的要创建的对象,都是符合某个标准的
interface Car{
void run();
}
class BMW implements Car{
public void run(){
…
}
}
class Benz implements Car{
public void run(){
…
}
}
//2、设计一个工厂类,用来创建各种各样的Car的实现类的对象
class CarFactory{
public static Car getCar(String name){
if(“宝马”.equals(name)){
return new BMW();
}else if(“奔驰”.equals(name)){
return new Benz();
}
return null;
}
} -
优点:代码简洁
-
缺点:当增加新产品时,需要修改工厂方法,违反了“对修改关闭,对扩展开放”的原则
-
-
工厂方法模式
-
示例
//1、所有的要创建的对象,都是符合某个标准的
interface Car{
void run();
}
class BMW implements Car{
public void run(){
…
}
}
class Benz implements Car{
public void run(){
…
}
}
//2、每一种产品都自己的工厂生产
//所有的工厂就有共同的特征
interface Factory{
Car getCar();
}
class BMWFactory implements Factory{
public BMW getCar(){
return new BMW();
}
}
class BenzFactory implements Factory{
public Benz getCar(){
return new Benz();
}
} -
优点:如果增加新产品,只需要增加对应的工厂即可,不需要修改原来的代码
-
缺点:类太多,代码复杂
-
-
抽象工厂模式
代理模式
-
代理类来控制被代理类的方法的访问
-
实现AOP的基础
-
静态代理
-
三个角色
-
主题(接口)
- 代理类与被代理类必须都实现主题接口
-
代理类
- 必须持有被代理类对象的引用
-
被代理类
-
-
示例代码
//主题
interface BuyHouse{
void trade();
}
//被代理者
class Real implements BuyHouse{
public void trade(){
…
}
}
//代理类
class Agent implements BuyHouse{
private BuyHouse target;public Agent(BuyHouse target){
this.target = target;
}public void trade(){
…前置工作
target.trade();
…后置工作
}
}
BuyHouse a = new Agent(new Real());
a.trade();
-
-
动态代理
-
三个角色
-
主题(接口)
- 代理类与被代理类必须都实现主题接口
-
代理类
- 代理类的创建方式变了,由类加载器对象,在运行时产生
-
被代理类
-
-
要点
-
1、主题
- 和静态代理模式一样,就是一个接口
-
2、被代理类
- 和静态代理模式一样,实现了主题的一个实现类
- 负责核心业务逻辑代码
-
3、处理器
- 用来描述,代理类要做的工作
- 必须实现一个InvocationHandler接口
- 重写一个public Object invoke(Object proxy,Method method,Object[] args)
- 还要持有被代理类的对象的引用
-
4、动态的创建代理类或代理类的对象
- Proxy.newProxyInstance(被代理类的类加载器,被代理类实现的所有接口,处理器对象);
- 被代理类的类加载器,用来动态的在内存中生成代理类的Class对象用,即动态生成代理类,
- 生成代理类时,要和被代理类实现同样的接口
-
-
示例代码
-
关键字的使用
this
-
表示当前对象
-
this调用结构:
-
this调用属性/方法:
- ① 在方法中:我们可以在方法中通过"this.属性"或者"this.方法"的方法,表示调用当前对象的指定属性或方法。但是通常情况下,我们都省略了"this."。但是如果方法的形参和当前对象的属性名相同的情况下,我们必须显式的使用"this.变量"的方式,表明此变量是属性,而非形参;
- ② 在构造器中:我们可以在构造器中通过"this.属性"或者"this.方法"的方法,表示调用当前正在创建的对象的指定属性或方法。但是通常情况下,我们都省略了"this."。但是如果构造器的形参和当前正在创建的对象的属性名相同的情况下,我们必须显式的使用"this.变量"的方式,表明此变量是属性,而非形参;
-
this调用构造器:
- ① 可以在类的构造器中使用"this(形参列表)"的方式,调用本类中重载的其他的构造器!
- ② 构造器中不能通过"this(形参列表)"的方式调用自身构造器;
- ③ 如果一个类中声明了n个构造器,则最多有 n - 1个构造器中使用了"this(形参列表)";
- ④ "this(形参列表)"必须声明在类的构造器的首行!
- ⑤ 在类的一个构造器中,最多只能声明一个"this(形参列表)";
-
super
-
表示父类的引用
-
super调用结构:
-
super调用属性/方法:
- ① 继承以后,我们可以在子类的方法或构造器中,通过"super.属性"或者"super.方法"的方式,显式的调用;父类中声明的属性或方法。但是很多情况下,我们都可以省略"super.";
- ② 如果子类和父类中出现了同名的属性,那么我们如果在子类中调用父类中同名的属性的话,必须使用"super."的方式;说明:子类的属性不会覆盖父类中同名属性。在堆空间中,两个属性都会存在;
- ③ 如果子类重写了父类中的方法,那么我们希望在子类中调用父类中被重写的方法,必须使用"super."的方式;
-
super调用构造器:
- ① 在子类的构造器的首行,显示的使用“super(形参列表)”的方式,调用父类指定的构造器;
- ② 针对于子类的某一个构造器而言,只能最多声明一个“super(形参列表)”;
- ③ 在子类的构造器的首行,要么使用“this(形参列表)” ,要么使用"super(形参列表)",不能同时出现;
- ④ 在子类构造器的首行,既没显式的使用“this(形参列表)”,也没显式的使用"super(形参列表)",那么默认提供的是:“super()”.;
- ⑤ 在子类的多个构造器中,至少一个构造器的首行使用了"super(形参列表)";
-
static
-
表示静态的修饰属性,方法,代码块,内部类
-
修饰
-
(1)属性
-
该属性表示所有该类的对象,共享
- 存储在“方法区”或“元空间”
- 其中一个对象对它修改,其他的对象的该属性值也会被修改
- 它的get/set也是static的
- 可以使用“显式初始化”,还可以使用“静态代码块”进行初始化
-
-
(2)方法
-
该方法是类方法
- 可以通过“类名.”调用
-
-
(3)代码块
- 静态代码块,在类加载并初始化时调用,并且只会执行一次
- 一般用于静态属性的赋值
-
(4)内部类
-
static修饰结构:
-
static修饰属性:
-
① 使用static修饰的变量,被类的所对象所共享:类的所对象共同同一个静态变量;
-
②不用static修饰的属性,称为:实例变量。每个对象拥一份实例变量。通过对象a修改某个实例变量,不会影响其他对象同名的实例变量的值;
-
③ 我们可以通过对象a去调用或修改静态变量的值,那么会影响其他对象对此静态变量的调用;
-
内存分配:
- 实例变量:随着对象的创建而产生,分配在堆空间中;
- 静态变量:随着类的加载而产生的,分配在方法区;
-
-
static修饰方法:
- ① 静态方法内:可以调用静态的属性或静态的方法。不可以调用非静态的属性和非静态的方法;
- ② 非静态的方法内:可以调用非静态的属性和非静态的方法。也可以调用静态的属性或静态的方法;
-
-
-
static 使用环境:
-
如何判定属性是否需要声明为static的?
- ① 是否需要类的多个对象来共享此属性;
- ② 很多常量都声明为static的;
-
如何判定方法是否需要声明为static的?
- ① 操作静态变量的方法;
- ② 工具类中的方法。(Math.random()😉;
-
final
-
表示最终的可以用来修饰变量、方法、类;
-
修饰
-
(1)类
- 该类不能被继承
-
(2)属性
-
该属性值不能被修改,即是常量属性
-
该属性必须手动初始化
- (1)显式初始化
- (2)构造块中初始化
- (3)在每一个构造器中初始化
-
-
(3)方法
- 该方法不能被重写
-
(4)局部变量
- 该局部变量的值一旦被赋值,就不能被修改,即常量
-
native
-
表示原生的
-
表示非Java语言实现的
- 但是使用者可以把它当做Java的方法或成员使用
new
- 表示创建对象
- 和构造器一起
instanceof
-
表示某个对象是否是本类或本类的子类的对象
-
例如:a instanceof Type
-
返回true
- a是Type的对象
- a是Type的子类的对象
-
-
instanceof
-
作用:
- 判断关键字左边对象是否是关键字右边类/接口的对象/实现类的对象,如果是返回true,如果不是返回false;在向下转型时,为了避免ClassCastException,利用instanceof关键字进行判断;
-
使用场景:
-
多态中的向下转型:
- ① 向下转型使用强制转化符:();
- ② 为了避免强转出现 Class Cast Exception 的异常,在强转前,使用 instance of 判断;
- ③ a instance of A : 判断对象 a 是否是类 A 的实例。如果 a 是类 A 的实例,返回 true。否则,返回 false;
-
-
extends
-
extends
-
语义:
- exteds 继承(延展 扩展);
-
使用场景:
- ① 类与类的继承之间使用的继承关键词;
- ② 实体类与实体类之间使用的继承关键字;
- ③ 实体类与抽象类之间使用的继承关键字;
- ④ 接口与实体类之间使用的继承关键字;
- ⑤ 接口与接口之间使用的继承关键字;
-
格式:
- class A extends B;
-
接口
-
为什么要声明接口?
-
(1)解决Java的单继承
例如:多功能的交通工具
既具有飞机的飞行功能,
又具有船的水上航行功能
又具有车的路上行驶功能 -
(2)多个不相关的类,可能具有共同的行为特征,这样的行为特征,就可以提取成一个接口
例如:
飞机有飞的能力
小鸟有飞的能力
…
飞机与小鸟之间没有is-a的关系
例如:
学生对象可比较
商品对象可比较
。。。
学生与商品没有is-a的关系,可比较行为可以抽取成接口- 补充了类与类之间的关系,对象与对象之间的关系,has -a 的关系,like -a的关系
- 因为继承只能表示is-a的关系
-
-
如何声明接口?
- 【修饰符】 interface 接口名{
}
- 【修饰符】 interface 接口名{
-
如何实现接口?
-
【修饰符】 class 实现类名 implements 接口名1,接口名2。。。{
} -
如果继承一个类与,实现接口同时存在,那么要先继承后实现
- 【修饰符】 class 子类 extends 父类 implements 接口名1,接口名2。。。{
}
- 【修饰符】 class 子类 extends 父类 implements 接口名1,接口名2。。。{
-
-
接口的特点
- (1)使用interface声明
(2)接口中只能有公共的抽象方法与全局静态常量
抽象方法的 public abstract可以省略,全局静态常量的public static final可以省略
(3)接口不能实例化,不能直接创建对象
(4)接口用来被实现的,它的实现类如果不是抽象类,那么必须实现接口的所有的抽象方法,如果实现类是个抽象类,可以保留抽象方法
(5)一个类可以实现多个接口(解决Java单继承的问题)
(6)接口没有构造器
(7)接口可以继承接口,而且可以继承多个接口
(8)接口与实现类的对象之间是多态引用
- (1)使用interface声明
-
接口的应用
- (1)
-
java8 中接口的新规定:
- ① 接口中可以定义静态方法、默认方法;
- ② 接口中定义的静态方法,只能通过接口进行调用;
- ③ 通过实现类的对象,调用接口中的默认方法;
- ④ 如果类实现的接口和继承的父类中,定义了同名同参数的方法,那么子类在没重写此方法的情况下,默认调用的是父类中声明的方法。—“类优先”原则;
- ⑤ 如果实现类实现了多个接口,接口中定义了同名同参数的方法。那么此时对于实现类来讲,会出现接口冲突问题;
- ⑥ 如何在实现类的方法中调用接口中的默认方法;
CompareA.super.method2(); - ⑦ (1)类实现接口以后,要么实现接口中的所抽象方法,方可实例化;
(2)要么当前类声明为抽象类(因为内部包含从接口中获取的抽象方法; - ⑧ 类可以继承多个接口,接口也可以继承接口,一定程度上解决了类的单继承性;接口与接口之间可以多继承也可以单继承;
- ⑨ 接口与具体的实现类之间存在多态性;实际上,接口定义了一套相应功能的规范与标准。
抽象类
-
为什么声明抽象类?
-
(1)在父类中明确子类应该包含某些方法,但是在父类中又不能给出具体的实现,父类中只能把这个方法声明为抽象方法,包含抽象方法的类必须是抽象类
abstract class 几何图形类{
public abstract double getArea();
}
class 圆 extends 几何图形类{
private double 半径;
public double getArea(){ return Math.PI * 半径 * 半径;
}
}
class 矩形 extends 几何图形类{
private double 长;
private double 宽;
public double getArea(){ return 长 * 宽;
}
}
原因:
(1)从逻辑角度
几何图形类中,应该包含所有几何图形共同的特征
那么所有几何图形,就应该包含 “获取面积”的功能
(2)语法角度
通过“几何图形”类对圆对象,矩形对象等的多态引用,
应该是可以调用“获取面积”的方法,如果父类中没有声明该方法,
就无法调用,无法实现多态引用
-
(2)如果父类是一个抽象的概念,虽然里面没有抽象方法,但是我不希望它被创建对象,那么可以把这个类声明为抽象类
-
-
如何声明抽象类?
-
格式
- 【修饰符】 abstract class 抽象类的名称{
类的成员列表
}
- 【修饰符】 abstract class 抽象类的名称{
-
-
抽象类的特点:
- (1)使用abstract修饰
(2)不能实例化,不能直接创建对象
(3)抽象类可以包含抽象方法,也可以没有抽象方法
(4)抽象类可以包含其他的成员:属性(类变量,实例变量)、方法(类方法,实例方法)、构造器(无参、有参,供子类调用)、代码块等
(5)抽象类用来被继承的,子类继承抽象类时,如果子类不是抽象类,那么必须重写/实现父类的所有抽象方法,如果子类是抽象类,那么可以保留抽象方法
(6)有抽象方法的类必须是抽象类
(7)抽象类与子类的对象之间是多态引用
- (1)使用abstract修饰
-
如何声明抽象方法?
- 【修饰符】 abstract 返回值类型 方法名(形参列表);
- 没有方法体
-
抽象类的应用
- (1)模板设计模式
-
注意:
- ① 前提:抽象性使用的前提是类的继承性;
- ② abstract关键字不能用来修饰:属性、构造器;
- ③ abstract关键字不能与 final , private , static 共用;
包装类:
-
包装类出现原因:
- 为了让基本数据类型的变量具有类中对象的特征;
-
类型间转化:
- ① 基本数据类型---->对应的包装类:自动装箱;
包装类---->对应的基本数据类型:自动拆箱; - ② 基本数据类型、包装类---->String 类:调用 String 的重载的方法:value Of( xxx x ); 123 + “”;
- ③ String类—>基本数据类型、包装类:调用包装类Xxx的parseXxx();
- ① 基本数据类型---->对应的包装类:自动装箱;
-
应用场景:
- vector.addElement(Object obj);//int—>Integer–>Object;
-
推广:
- 很多方法的形参都是声明为类的类型。那么只能将本类或本类的子类的对象作为实参传入。那么就限制了传入基本数据类型的变量。为了能操作基本数据类型,我们需要对基本数据类型进行封装,封装为对应的包装类的对象,进而就满足形参中对类的要求;
-
基本数据类型与对应包装类:
- 基本数据类型 包装类
- boolean Boolean
- char Character
- byte Byte
- short Short
- int Integer
- long Long
- float Float
- double Double
package
-
包的作用
- (1)避免类的重名
(2)限定访问权限,如果希望某个类或成员仅限于本包使用,那么该类或成员的权限修饰符是缺省的
(3)便于管理,把功能相近的类,放在一个包中,分类管理
- (1)避免类的重名
-
包的声明
-
package 包名;
-
注意
-
(1)必须在源文件的第一行
-
(2)一个源文件只能有一句声明包的语句
-
(3)包名的命名习惯
- 包名的命名规范:
(1)域名倒置+分类的包名
(2)全部单词都小写,每个单词之间使用.
- 包名的命名规范:
-
-
-
包的导入
-
标准格式
- import 包.类名;
-
简写格式:
-
import 包.*;
- 此处的*只能代表类,不包括子包
-
-
静态导入
-
import static 包.类.*;
- 此处的*代表类中的静态成员(静态方法和静态的属性)
- 意味着可以在本类中,直接使用被导入的静态成员,而不需要加“类名.”
-
-
-
如果一个源文件声明了包
-
编译的格式:
-
javac -d 字节码文件生成的存储目录 源文件名.java
-
例如:javac -d ./ 源文件名.java
- ./表示源文件的当前目录
-
-
运行的格式
- java 包.类名
-
-
JDK中常见的包
-
- java.lang----包含一些Java语言的核心类,如String、Math、Integer、System和Thread,Object,提供常用功能。
-
- java.net----包含执行与网络相关的操作的类和接口。
- java.io ----包含能提供多种输入/输出功能的类。
- java.util----包含一些实用工具类,如定义系统特性、接口的集合框架类、使用与日期日历相关的函数。
- java.text----包含了一些java格式化相关的类
- java.sql----包含了java进行JDBC数据库编程的相关类/接口
- java.awt----包含了构成抽象窗口工具集(abstract window toolkits)的多个类,这些类被用来构建和管理应用程序的图形用户界面(GUI)。
-
java.applet----包含applet运行所需的一些类。
面向对象三大特征
封装性
-
广义的封装
- 1、方法:把功能的实现,封装到方法中,对于使用这个方法者来说,隐藏方法的实现,暴露方法的签名,可以通过方法的签名调用即可
- 2、把一个事物的数据描述,行为描述封装到一个类中
- 3、包:如果某个类或成员的修饰符是“缺省”的,这个类或成员就仅限于本包使用
- 4、组件的封装:例如:网银支付,支付宝等等,提供给使用者的是“接口”
- 5、系统的封装:
-
狭义上体现:
-
① 私有化(private)类的属性xxx,同时,提供公共的(public)设置(setXxx())和获取(getXxx())的方法;
-
② 如果类中的方法,不希望在类外被调用,我们可以设置为 private 的;
-
③ 如果类A不希望在本包之外被调用,那么可以将此类A声明为缺省的;
-
④ 单例模式;
-
饿汉模式;
//饿汉式1
class Bank{//1.私化构造器
private Bank(){}//2.内部提供一个当前类的实例
//4.此实例也必须静态化
private static Bank bank = new Bank();//3.提供公共的静态的方法,返回当前类的对象
public static Bank getInstance(){
return bank;
}
} -
懒汉模式;
//懒汉式:
class Bank1{//1.私化构造器
private Bank1(){}//2.声明当前类的实例
//4.此实例也必须静态化
private static Bank1 bank = null;//3.提供公共的方法获取当前类的实例
public static Bank1 getInstance(){
if(bank == null){bank = new Bank1(); } return bank;
}
}
-
-
-
隐藏了实现细节,仅暴露使用的方式
-
作用:
- 安全,使用简便,隔离它的变化
-
四种权限修饰符:
-
从大到小:
- private < 缺省 < protected < public;
-
权限修饰符可修饰的结构:
- ① 4种权限都可以用来修饰类的内部结构:属性、方法、构造器、内部类;
- ② 只能使用缺省 或者 public 来修饰外部类 ;
-
修饰范围:
- 修饰符 类内部 同一包 不同包的子类 任何地方
- private yes
- (缺省) yes yes
- protected yes yes yes
- public yes yes yes yes
-
继承
-
为什么要继承
- 1、当多个类之间具有共同的特征,那么可以把这个共同的部分抽取成一个父类,这些类就可以通过继承这个父类,而达到“代码复用”,这样就不用在多个类中重复声明相同的部分
- 2、模拟,表现现实世界中一个“is-a”的关系
-
如何实现继承?
-
extends
- 扩展
-
语法格式
- 【修饰符】 class 子类名 extends 父类名{}
-
-
继承的特点
-
1、子类会继承父类的所有的属性和方法,但是不会继承父类的构造器
- 子类也会继承父类的私有的属性与方法,但是在子类中不能直接使用父类的私有成员
-
2、子类一定会调用父类的构造器
- (1)如果子类构造器中,没有写super()或super(实参列表)语句,那么默认就是调用父类“无参构造”
- (2)子类的构造器中,也可以手动调用父类的构造器,super()或super(实参列表)
- (3)当父类没有无参构造时,子类的构造中必须手动调用父类的有参构造super(实参列表)
- super()或super(实参列表)它必须在子类构造器的首行
-
3、Java中只支持单继承,但是又支持多层继承
- (1)Java类只有一个直接父类
- (2)Java类又支持代代相传
- (3)Java类的根父类是java.lang.Object
-
类的继承
-
子类继承父类后的不同:
- ① 子类A继承父类B以后,就获取了父类中声明的结构:属性、方法;
- ② 不管父类中的属性或方法的权限为何,子类都可以获取到。 但是由于封装性的影响,可能在子类中不能直接调用父类中权限较小的属性或方法。
- ③ 子类可以在继承父类的基础之上,再提供自己特的属性或方法;
-
子类实例化的全过程:
- ① 子类在继承父类以后,就获取了所的父类中声明的属性和方法;
- ② 实例化子类对象的过程中,间接或者直接的调用父类的构造器,或者父类的父类的构造器。因为加载了这些父类的结构,我们在子类对象的内存中调用父类声明的属性和方法;
- ③ 在子类对象整个实例化过程中,只创建了唯一的一个对象,即为当前的子类对象;
-
-
-
作用:
- (1)代码复用
- (2)表示“is-a”的关系
-
继承的体现
-
为什么有继承:
- 多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可;
-
继承的格式:
- class A extends B 其中:A 为子类 B 为父类又名基类、超类
-
继承性说明:
- ① 子类和父类是相对的概念,父类又继承自己的父类,所有的类直接或间接的继承 lang 包下的 Object 类;
- ② 子类通过继承,可以获取直接父类以及所的间接父类中声明的所的属性和方法;
- ③ 一个父类可以有多个子类,但子类只能声明一个父类;
- ④ 如果一个类没有显式继承另外一个类的话,则其父类为:java.lang.Object类(根基类);
- ⑤ 任何一个类在继承java.lang.Object类之后,就获取了 Object 类中声明的属性和方法;
- ⑥ java 中,继承是对父类的扩展,而不是父类的子集;
-
继承的优点:
- ① 减少了代码的冗余,提高了复用性;
- ② 具有更好的扩展性,继承的出现让类与类之间产生了关系,提供了多态的前提;
- ③ 不能为了继承而去继承,不要仅为了获取其他类中某个功能而去继承;
-
继承的规则:
- 注意:子类不能直接访问父类中私有的(private)的成员变量和方法;
-
多态
-
表现
-
(1)方法的多态性:方法的重写
-
(2)对象的多态性
-
编译时类型与运行时类型不一致
-
前提
- (1)继承+重写
- (2)多态引用:父类的变量指向子类的对象
-
最终表现:编译时按照父类的类型进行编译检查,运行时,执行的是子类“重写”的代码
-
-
是实现多态绑定技术的前提
-
-
-
数据类型的转换
-
向上转型
-
多态引用
- 父类的变量指向了子类的对象,编译时呈现出子类的对象向上转型成父类
-
自动完成
-
-
向下转型
-
强制转换
-
子类类型 变量 = (子类的类型)父类的变量;
-
要保证转型成功
-
这个父类的变量必须是指向该子类的对象
-
在转换之前加判断:
- if(变量 instanceof 子类的类型){
才可以向下转型成这个子类
}
- if(变量 instanceof 子类的类型){
-
-
其他的都失败
- (1)父类的变量指向父类的对象
- (2)父类的变量指向其他子类的对象
- 失败报异常:java.lang.ClassCastException类型转换异常
-
-
-
-
-
多态的作用:代码更灵活
-
多态的应用
- 1、多态参数
- 2、多态数组
- 3、多态属性
-
面试题
-
1、static的方法
-
static的方法不能被重写,因此也不符合多态的特征
-
示例
package com.atguigu.review;
/*
-
static与多态
-
static的方法,是不能被重写
*/
public class TestStatic {
public static void main(String[] args) {
Father.method();//执行的是父类的方法 Son.method();//子类的方法 System.out.println("------------------"); Father f = new Father();//本态引用 f.method();//可以这么调用 执行父类 System.out.println("------------------"); Son s = new Son();//本态引用 s.method(); //执行子类 System.out.println("------------------"); Father father = new Son();//多态引用 father.method(); //按照多态,应该执行的是子类的重写的方法 此处不符合多态的特点
}
}
class Father{
public static void method(){
System.out.println("父类的静态方法");
}
}
class Son extends Father{
//尝试重写父类的静态方法 ,但是其实是子类自己的方法,不是重写父类的方法
//@Override 注解的作用,是按照重写的标准来检查该方法是否符合重写的要求
public static void method(){
System.out.println("子类的静态方法");
}
}
-
-
-
2、属性
-
属性不能被覆盖,因此也不符合多态的特征
-
示例
package com.atguigu.review;
/*
- 属性没有重写一说,没有多态
*/
public class TestField {
public static void main(String[] args) {
Dad d = new Dad(); System.out.println(d.name);//Dad Baby b = new Baby(); System.out.println(b.name);//baby Dad dd = new Baby(); System.out.println(dd.name);//Dad
}
}
class Dad{
String name = “Dad”;
}
class Baby extends Dad{
String name = “baby”;
}
-
-
3、可变参数
-
示例
package com.atguigu.review;
public class TestSub {
public static void main(String[] args) {
Base base=new Sub();//多态引用 base.add(1,2,3);//2,3已经按照数组去编译
}
}
class Base {
public void add (int a, int… arr ){
System.out.println("base");
}
}
class Sub extends Base{
public void add(int a, int[] arr ){
System.out.println("sub_1");
}
public void add(int a , int b, int c ){
System.out.println("sub_2");
}
}
-
-
Object类
使用说明:
- ① Object是所有其他类的根父类;
- ② Object类中只定义了一个Object();
- ③ Object类中定义的方法适用于任何其子类,包括数组;
equals():
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String aString = (String)anObject;
if (!COMPACT_STRINGS || this.coder == aString.coder) {
return StringLatin1.equals(value, aString.value);
}
}
return false;
}
- ① java.lang.Object类中的equals()方法的定义:
public boolean equals(Object obj) {
return (this == obj);
} - ② 像String、Date、File、包装类等重写了Object类中的equals()方法,比较两个对象中的实体内容是否相等;
- ③ 对于自定义类来讲,如果没重写Object类中的equals()方法,调用equals()方法时,仍然比较两个对象的引用地址是否相同;
- ④ 一般情况下,在开发中一旦调用了自定义类的equals(),通常都是重写以后的equals()方法;
- ⑤ 重写equals()的规则:比较两个对象的属性是否都相等;
toString():
- ① java.lang.Object类中toString()定义如下:
public String toString() {
return getClass().getName() + “@” + Integer.toHexString(hashCode());
} - ② 当我们打印一个对象的引用时,实际上就是调用了其toString();
- ③ 像String、Date、File、包装类等重写了Object类中的toString(),返回其代表的具体内容;
- ④ 对于自定义类而言,如果我们没有重写Object类中的toString()方法,则返回的仍然是地址值;
- ⑤ 如果重写的话,重写的规则:返回当前对象的属性信息;
相关面试题
方法的重写和重载的区分?
override / overwrite / overload;
重写:
概念:子类重写的方法;父类被重写的方法
方法的声明: 权限修饰符 返回值类型 方法名(形参列表) throws 异常的类型 { }
子类重写的方法 与 父类被重写的方法的方法名和形参列表都相同
子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符
特别的,子类不能重写父类中声明为 private 的方法。
子类重写的方法的返回值类型 不大于 父类被重写的方法的返回值类型。
举例:1.父类方法的返回值类型是void,子类要想重写,一定也是 void
2.父类方法的返回值类型是类型A,子类重写父类方法,返回值类型可以是类型A或类型A的子类.不能是类型A的父类
子类重写的方法抛出的异常类型 不大于 父类被重写的方法抛出的异常类型
规则: 子类与父类中同名同参数的方法必须同时声明为 static 的(不是重写),或者同时声明为非 static 的(即为重写。
重载:
① 在同一类中,声明多个方法名相同参数列表不同的变量;
② 参数列表不同,或者参数声明顺序不同也可称为方法的重载;
谈谈对多态的理解?
1.多态性的理解:可以理解为一个事物的多种形态。
2.广义上多态性的体现:
①方法的重载 和 方法的重写
②子类对象的多态性
3.狭义上多态性的体现:
子类对象的多态性
4.何为子类对象多态性:父类的引用指向子类的对象。(子类的对象赋给父类的引用)
举例:前提:Student和Man都是Person类的子类
Person p = new Student();
Person p = new Man();
Object obj1 = new String(“ABC”);
Number n = new Integer(1);
5.多态性的应用:
虚拟方法调用:编译时,认为调用的方法是父类的,但是当运行时,实际执行的是子类重写父类的方法.
说明:多态中方法的调用:编译看左边,运行看右边!
6.多态性的说明:
多态性,只适用于方法。不适用于属性的!
== 和 equals()区别?
A:==:使用范围:可以操作基本数据类型 和 引用数据类型;
如果操作的是基本数据类型:比较两个基本数据类型的变量对应的值是否相等。
如果操作的是引用数据类型:比较两个引用的地址是否相同。
B : equals():使用范围:只适用于引用数据类型;
1.java.lang.Object类中的equals()方法的定义:
public boolean equals(Object obj) {
return (this == obj );
}
说明:Object 类中 equals()比较两个对象的引用地址是否相同。(或:比较两个引用是否指向同一个对象实体)
2. 像 String、Date、File、包装类等重写了 Object 类中的 equals()方法,比较两个对象中的实体内容是否相等。
-
对于自定义类来讲,如果没重写 Object 类中的 equals()方法,调用 equals()方法时,仍然比较两个对象的引用地址是否相同。
-
一般情况下,在开发中一旦调用了自定义类的 equals(),通常都是重写以后的 equals()方法。
5.重写equals()的规则:比较两个对象的属性是否都相等。
throws 和 throw 的区别?
throws throw
① 自动抛出异常; ① 手动抛出异常
② 此异常抛给父类, ② 在方法中抛出异常;
并没有进行解决; ③ 可以抛出自定义的异常信息;
③ 在最后的父类(main)
方法中运用 try-catch-finally
来解决异常;
④ 在方法的声明中抛出异常
给父类;
接口与抽象类的区别?
接口:
1.接口(interface) 是与类(class)并列的结构;
2.如何定义一个接口.使用interface关键字;
3.接口中只能定义常量和抽象方法 ----> JDK7.0及以前。 (JDK8.0接口中引入默认方法、静态方法)
常量的定义:public static final 修饰。
抽象方法的定义:public abstract 修饰
4.接口中不能声明构造器! 接口是不可以直接实例化的。
5.类与接口之间的关系:实现(implements)关系. 类实现接口以后,就获取了接口中声明的结构:常量、抽象方法
格式:class SubClass extends SuperClass implements Interface A
6.类实现接口以后,要么实现接口中的所抽象方法,方可实例化。
要么当前类声明为抽象类(因为内部包含从接口中获取的抽象方法;
7.java规定:类可以实现多个接口。—>一定程度上,解决了 java 类的单继承的局限性。
8.java规定:接口与接口之间是继承关系。而且是可以多继承的!
9.接口与具体的实现类之间存在多态性;
10.接口,实际上可以理解定义了一套相应功能的规范、标准。
抽象类:
abstract 修饰类:抽象类
>不可实例化
>仍然存在构造器。构造器的作用:便于子类对象实例化时进行调用
>通常情况,我们会提供抽象类的子类,让子类实例化,调用父类的结构。
>抽象类中不一定有抽象方法;抽象方法所在的类,一定是抽象类。
abstract 修饰方法:抽象方法(没方法体,此方法一定不能被调用)
>如果子类重写了父类中的所的抽象方法,则此子类可以实例化
>如果子类没重写父类中的所的抽象方法,则子类仍为抽象类
XMind - Evaluation Version