第4章 面向对象

简介: 面向对象的方方面面(异常单独列一章)。

4.0 面向对象的学习内容

4.1  类和对象

  1. 类和对象是面向对象的核心概念。
  • 类是对一类事物的描述,是抽象、概念上的定义。
  • 对象是实际存在的该类事物的每个个体,因而也被称为实例。
  1. 类和对象的书面理解:
  • Java语言范畴中,将功能、结构等封装到类中,通过类的实例化,来调用具体的功能、结构。
  • Scanner、String等
  • 文件:File
  • 网络资源:URL
  • 涉及到Java语言与HTML、后端数据库交互时,前后端的结构在java层面交互时,都体现为类、对象。

4.1.1 类

  1. 类的语法格式:
修饰符class类名{
属性声明;
方法声明;
}
publicclassPerson{
privateintage ; //声明私有变量 agepublicvoidshowAge(inti) { //声明方法showAge( )age=i; 
    } 
}
  1. 类的成员:
  • 属性:对应类中的成员变量
  • Field = 属性 = 成员变量
  • 行为:对应类中的成员方法
  • Method = 方法 = 函数
  1. 类权限修饰符只能缺省或public

4.1.2 类的成员——属性

  1. 属性的定义(声明):语法同变量:访问修饰符 属性类型 属性名;
  2. 细节和注意事项:
  • 访问修饰符:public、proctected、默认、private。
  • 属性的类型:可以是任意类型,包括基本数据类型和引用数据类型。
  • 属性的默认值:属性不赋值时,有默认值,规则同数组
  • byte、byte、short、int、long:0
  • float、double:0.0
  • char:\u0000(空字符)
  • boolean:false
  • String:null
  1. 属性赋值:遵循变量赋值的规定,即:
  • 如果变量是基本数据类型,此时赋值的是变量所保存的数据值。
  • 如果变量是引用数据类型,此时赋值的是变量所保存的数据的地址值。

4.1.3 类的成员——方法

  1. 方法的定义:
访问修饰符返回数据类型方法名(形参列表……){
语句;
return返回值;
}
  • 形参列表:表示成员方法的输入
  • 返回数据类型:表示成员变量的输出类型。void表示没有返回值。
  • {}:表示方法体,实现某一功能的代码块
  • return语句:不是必须的。
  • 无返回值类型的方法也可以用return,用于结束方法。
  1. 细节和注意事项
  • 访问修饰符:
  • public、proctected、默认、private
  • 控制方法的使用范围。
  • 返回数据类型:
  • 一个方法只能有一个返回值。返回多个值可以使用数组接收。
  • 返回类型可以是任意类型包含基本类型或引用类型(数组、对象)
  • 如果要求有返回值,则必须有return语句,且return语句的数据类型与方法的返回数据类型一致或兼容。
  • 如果方法时void,则方法体中可以没有return语句,或者只写return。
  • 方法名:
  • 见名知义
  • 小驼峰命名方法,遵循标识符的规则、规范
  • 形参列表:
  • 一个方法可以有0个参数,也可以有多个参数,多个参数用逗号分开。
  • 参数类型可以是任意类型,包括基本类型和引用类型。
  • 调用带参数的方法时,必须传入对应类型或兼容类型的实参。
  • 调用方法时,实参和形参的数量、类型、顺序必须一致。
  • 方法体:
  • 方法体的语句可以为输入、输出、变量、运算、分支、循环、方法调用,但是不能嵌套定义——方法中不能再声明方法。
  1. 方法调用的细节
  • 同一个类中的方法A(非main方法)中调用方法B时,可以直接调用方法名()
  • main方法需要调用同一个类中的方法B时,需要通过实例化类,对象名.B方法名()调用
  • 跨类中的方法A调用方法B时,需要通过对象名(实例化对象)调用类名 对象名 = new 类名();对象名.方法名()
  • 跨类调用的方法和它的访问修饰符有关。
  1. 方法的调用机制:
  • 当程序执行(main方法)到方法时,会在jvm的栈内存内开辟一个独立的空间
  • 当方法执行完毕时,或执行到return语句时,就会将方法的运行结果返回给栈内存中调用方法的地方。同时销毁开辟的独立空间。
  • 返回后,程序继续执行后面的代码
  • 当jvm内存内的main方法执行完毕,整个程序退出。
  1. 使用方法的优点:
  • 提高了代码的复用性。
  • 将实现的细节封装起来,供其他用户调用。
  1. 方法的传参机制(值传递):
  • 基本数据类型:传递的是值(值拷贝),形参的任何改变不影响实参。
  • 引用数据类型:传递的是地址,可以通过形参影响实参。
  • 形参不影响实参的情况:形参在方法内开辟了新的内存空间(需要在方法体内的语句实现)。
  • 字符串的传参?
  1. 克隆对象:创建两个独立的对象,只是属性、方法一致。
  • 利用引用数据类型传参的机制(在方法体内创建新对象,逐一赋值)。

4.1.4 类的实例化——对象

  1. 创建对象:
  • 方式一:类名 对象名 = new 类名();
  • 方式二:类名 对象名;对象名 = new 类名();
  • Person p1 = new Person();
  • p1是对象的名(对象的引用),保存实例化对象的内存地址
  • new Person()创建的对象空间(数据)才是真正的对象
  • 方式三:创建对象数组类名[] 对象数组名 = new 类名[n];
  1. 访问成员:对象名.对象成员
  • 访问属性:对象名.属性
  • 访问方法:对象名.方法()
  1. 匿名对象:
  • 含义:创建对象时,如果没有显式地给对象起名,只是进行了new 类名(),则new 类名()为匿名对象。
  • 特征:匿名对象只能调用一次。
  • 第二次调用时已经是新对象了,跟前一个对象没有关系。
  • 使用:
  • 临时使用、不需要保留
  • 作为方法的参数

4.1.5 方法重载(OverLoad)

  1. 定义:在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或者参数类型不同即可。
  2. 判断是否是重载:
  • “两同一不同”:
  • 在同一个类中
  • 相同方法名
  • 参数列表不同:
  • 参数个数不同
  • 参数类型不同
  • 同类型参数顺序不同
  • 跟方法的权限修饰符、返回值类型、形参变量名、方法体都没有关系!
  1. 在通过对象调用方法时,如何确定某一个指定的方法:
  • 判断方法名是否一致
  • 判断参数列表是否一致:参数类型、参数个数、参数顺序一致
  1. 注意点:子类可以重载父类同名不同参的方法。

4.1.6 方法递归(Recrusion)

  1. 含义:方法自己调用自己,每次调用时传入的参数不同
  2. 调用规则:
  • 递归执行一次,会在栈内存内创建一个受保护的独立空间(栈空间)
  • 方法的局部变量相互独立,不会相互影响
  • 方法中使用引用数据类型的变量时,就会共享该引用类型的数据。
  • 递归必须向退出递归的条件逼近,否则就是无限递归。
  • 当一个方法执行完毕,或者遇到return语句,就会返回,遵循谁调用,结果就返回给谁。同时该方法执行完毕或返回时,该方法也就执行完毕。

4.1.7 构造方法/构造器(Constructor)

  1. 基本语法:[修饰符] 方法名(形参列表){}
  2. 语法说明:
  • 修饰符可以默认(无),也可以用private、protected、public。
  • 修饰符为默认(无)时,与类的修饰符一致。
  • 构造器没有返回值,也不可以写void
  • 方法名必须和类名一模一样
  • 参数列表和方法的规则一致
  • 在创建对象时,系统会自动调用该类的对象完成属性的初始化。
  • 完成对象的初始化不是创建对象
  • 不能被static、final、synchronized、abstract、native修饰,不能有return语句返回值
  1. 主要作用:完成新对象(属性)的初始化。
  2. 构造器重载:
  • 一个类可以定义多个构造器
  • 如果一个构造器没写,系统会自动生成一个无参、默认的构造器(该构造器的修饰符与类一致)。
  • 一旦定义了一个构造器,默认的无参构造器就不能使用了,使用了会报错。
  • 如果想继续使用,需要再重新显式定义一下。
  1. 对象创建流程:
  • 加载类信息,加载到方法区,只会加载一次
  • 在堆空间中分配空间(地址)
  • 完成对象的初始化
  • 默认初始化
  • 显式初始化
  • 构造器初始化
  • 将对象在堆空间的地址返回给创建对象时定义的对象
  1. 父类的构造器不可被子类继承
  2. 注意点:使用了构造器后,实例化对象要用构造器的方法,否则会报错。
  • 原因为:显式声明构造器后,默认的无参构造器会不可用。

4.1.8 方法重写(override/overwrite)

  1. 定义:子类继承父类以后,可以对父类中同名同参数的方法,进行覆盖操作
  2. 应用:重写以后,当创建子类对象以后,通过子类对象调用子父类中的同名同参数的方法时,实际执行的是子类重写父类的方法。
  3. 语法:权限修饰符 返回值类型 方法名(形参列表) throws 异常的类型{}
  • 方法名:父子一致。
  • 形参列表:父子一致。
  • 权限修饰符:子类不小于父类
  • 不能重写父类中private修饰的方法,因为private修饰的方法对外部不可见。
  • 返回值类型:
  • void:父子一致。
  • 基本数据类型:父子一致。
  • 引用数据类型:子类不大于父类
  • 异常类型:子类不大于父类
  1. 注意点:子类和父类中的同名同参数的方法要么都声明为非static的(考虑重写),要么都声明为static的(不是重写)。

4.1.9 代码块

  1. 作用:用来初始化类、对象
  2. 修饰:
  • 权限修饰:只能缺省。
  • 关键字:只能用static或没有。
  1. 分类:静态代码块与非静态代码块,
  • 相同点:
  • 都可以用于对类的属性、声明初始化
  • 都可以声明多个代码块,但一般每种最多写一个
  • 多个代码块默认执行顺序都是先上后下
  • 不同点:
  • 静态代码块:
  • 只能调用静态的属性和方法
  • 随类的加载而执行,只执行一次
  • 类加载的三个时机:
  • 创建对象实例时(new)
  • 创建子对象实例时,父类会被加载
  • 使用类的静态成员时。
  • 非静态代码块:
  • 既可以调用静态的属性和方法,也可以调用非静态的属性方法
  • 随对象的创建而执行,创建一次执行一次。先于构造器执行
  • 可以将构造器相同的部分写到代码块内,减少不同构造器间的代码冗余。
  1. 创建对象时,类的调用顺序:
  • 静态代码块和静态属性。取决于书写顺序。
  • 普通代码块和普通属性。取决于书写顺序。
  • 构造器。
  1. 创建子类对象时,类的调用顺序:
  • 父类的静态代码块和静态属性。取决于书写顺序。
  • 子类的静态代码块和静态属性。取决于书写顺序。
  • 父类的普通代码块和普通属性。取决于书写顺序。
  • 父类的构造器。
  • 子类的普通代码块和普通属性。取决于书写顺序。
  • 子类的构造器。

4.1.10 内部类(InenerClass)

  1. 含义:定义在类内部的一个类,包含内部的类叫外部类
  • 外部引用时需要完整写出类名称(含包名)
  • 编译以后生成OuterClass$InnerClass.class字节码文件
  1. 分类:
  • 局部内部类:定义在方法、代码块、构造器中。
  • 成员内部类(static修饰和无修饰):定义在成员位置,类内可定义类的五大组成部分(属性、方法、构造器、代码块、内部类)
  • 可以被final修饰,表示此类不能被继承。
  • 可以被abstract修饰,表示不能被实例化
  1. 局部内部类:
  • 可以直接访问外部类的所有成员,包含私有的
  • 但是如果调用局部内部类所在方法中的局部变量时,要求该方法中的局部变量为final修饰,JDK8之前显式声明,JDK8之后可省略。
  • 不能添加权限修饰符(只能缺省,同局部变量的修饰符范围),可以被final修饰,修饰后不可被继承
  • 不能被static修饰,也不能包含static成员
  • 内部类的成员与外部类的成员重名时,内部类调用遵循就近原则,需要调用外部成员时,需要通过外部类.this.成员名的方式
  • 由于局部内部类定义在方法、代码块、构造器中,实际上是一个局部变量,只能在定义它的位置生效,即只能在这个位置实例化。
  • 外部类需要访问内部类的成员时,需要通过上述流程,在外部类的方法中,将内部类实例化。
  • 外部其他类不能访问。
  1. 成员内部类:
  • 可以直接访问外部类的所有成员,包含私有的
  • 使用static修饰时,只能调用外部类声明为static的结构
  • 非静态内部类,内部不能声明静态成员
  • 前面省略了外部类.this.,不能使用this.
  • 可以使用四种权限修饰符修饰
  • 实例化内部类:
  • 静态内部类:外部类.内部类 变量名 = new 外部类.内部类();
  • 非静态内部类:外部类.内部类 变量名 = 外部类的引用.new 内部类();
  • 外部类.内部类 变量名 = 外部类的引用.new 外部类.内部类();
  • 内部类的成员与外部类的成员重名时,内部类调用遵循就近原则,需要调用外部成员时,需要通过外部类.this.成员名的方式,调用内部类自身的属性,this.成员名
  1. 匿名内部类:
  • 语法:new 父类构造器(实参列表)|实现接口(){ //匿名内部类的类体部分 };
  • 可以基于接口实现、也可基于父类实现
  • 分号不能少
  • 可以是成员内部类、也可以是局部内部类。
/*** @date 2022年5月24日下午9:25:26*/packagecom.infinity.interfacetest;
publicclassCellphone{
// 1.1 匿名内部类是成员内部类doubleb=newCalculator() {
doublec=9.0;
@Overridepublicdoublework() {
// TODO Auto-generated method stubreturn0;
        }
    }.c;
// 1.2 匿名内部类是成员内部类doublea=newCalculator() {
@Overridepublicdoublework() {
// TODO Auto-generated method stubreturn0;
        }
    }.work();
publicdoubletestWork(Calculatorc) {
doubleresult;
result=c.work();
// 2.1 匿名内部类是局部内部类doubleb=newCalculator() {
doublec=9.0;
@Overridepublicdoublework() {
return0;
            }
        }.c;
// 2.2 匿名内部类是局部内部类doublea=newCalculator() {
@Overridepublicdoublework() {
// TODO Auto-generated method stubreturn0;
            }
        }.work();
returnresult;
    }
publicstaticvoidmain(String[] args) {
Cellphonecp=newCellphone();
// 2.3 匿名内部类是局部内部类doublea=cp.testWork(newCalculator() {
@Overridepublicdoublework() {
return1+1;
            }
        });
System.out.println(a);
    }
}
  • 匿名内部类本身也是一个对象,因此它也可以调用内部类内部的方法,执行时遵循多态的规则(匿名内部类重写了就执行内部类里的方法)
  • 其他有关问题参见4.4.8第6部分。
  1. 外部类不能被static修饰
  2. 成员内部类和局部内部类,在编译以后,都会生成字节码文件。
  • 成员内部类:外部类$内部类名.class
  • 局部内部类:外部类$数字 内部类名.class

4.2 jvm内存分析

4.2.1 内存结构

  1. 图结构:
  1. 引用类型的变量只能存储两类值:
  • null
  • 包含变量类型的地址值。

4.2.2 内存分配

  1. 栈:一般指虚拟机栈,存储局部变量。
  2. 堆:存放对象(含对象的属性)、数组。
  3. 方法区:常量池(常量、字符串)、静态变量、即时编译后的代码。

4.2.3 成员变量与局部变量的异同

  1. 相同点:
  • 定义变量的格式相同:数据类型  变量名 = 变量值
  • 先声明,后使用
  • 都有其对应的作用域以及生命周期
  1. 不同点:
  • 在类中声明的位置的不同
  • 属性:直接定义在类的一对{}
  • 局部变量:声明在方法内、方法形参、代码块内、构造器形参、构造器内部的变量
  • 作用域范围不同:
  • 属性:可以被本类使用、也可以被其他类使用(通过对象调用)
  • 局部变量:只能在本类中对应的方法使用。
  • 关于权限修饰符的不同
  • 属性:可以在声明属性时,指明其权限,使用权限修饰符。
  • 局部变量:不可以使用权限修饰符。
  • 默认初始化值的情况不同:
  • 属性:类的属性,根据其类型,都有默认初始化值。
  • 局部变量:没有默认初始化值。在调用局部变量之前,一定要显式赋值。形参在调用时赋值即可。
  • 在内存中加载的位置不同:
  • 属性:加载到堆空间中(非static),因为对象加载到了堆空间
  • 局部变量:加载到栈空间
  • 生命周期不同:
  • 属性:生命周期长,伴随对象的创建而创建,伴随对象的销毁而销毁。
  • 局部变量:生命周期短,伴随代码块的执行而创建,伴随代码块的执行结束而销毁。
  1. 属性和局部变量可以重名,访问时遵循就近原则。

  1. 属性赋值过程:
  • 默认初始化
  • 显式初始化 / 代码块
  • 取决于类中书写的顺序
  • 构造器中初始化
  • 通过“对象.属性“或“对象.方法”的方式赋值

4.2.4 可变个数形参

  1. 语法:
  • JDK 5.0以前:采用数组形参来定义方法,传入多个同一类型变量
  • public static void test(int a, String[] books);
  • JDK5.0:采用可变个数形参来定义方法,传入多个同一类型变量
  • public static void test(int a, String… books);
  1. 注意点:
  • 调用方法时,可传入的参数个数可以是0个,1个或多个
  • 可变参数的方法可与其他方法构成重载,调用时,优先调用匹配度高的
  • ...[]作为方法参数的同名方法视为同一个类里视为重复定义(编译报错),父子类内视为重写。
  • 可变参数方法的使用与方法参数部分使用数组是一致的,方法体内使用for循环
  • 方法的参数部分有可变形参时,需要放在形参声明的最后
  • 一个方法最多只能声明一个可变个数形参

4.3 封装、继承和多态

2.3.1 封装与隐藏

  1. 含义:将类中的属性(数据)私有化,并提供外部可访问的类进行属性操作。
  2. 好处:
  • 对(变量)数据进行验证,保证数据安全
  • 隐藏实现细节
  • 便于修改,增强代码可维护性
  1. 体现:
  • 将属性使用private修饰,进行私有化,并提供公共的set和get方法获取和设置此属性的值
  • 提供public修饰的setter,用于判断并修改属性,无返回值
  • 提供public修饰的getter,用于判断并读取属性,有返回值
  • 不对外暴露私有方法
  • 单例模式(将构造器私有化)
  • 如果不希望类在包外调用,可以将类设置为缺省的
  1. 权限修饰符:

修饰符

类内部

同一个包

不同包的子类

(其父类是protected)

同一个工程

private




缺省



protected


public

  • 类的权限修饰符只能用public或缺省
  • 使用public时,本工程下使用
  • 需要导入全类名
  • 使用缺省时,只可以被同一个包内部的类访问
  • 局部变量(方法内、方法形参等)的修饰符只能缺省。
  • protected修饰的属性、方法需要在不同包内访问时,一是需要父子继承关系,且protected修饰的内容是父类,二是需要在子类中导入包

4.3.2 继承(inheritance

  1. 作用:
  • 减少代码的冗余,提高代码的复用性,提高开发效率
  • 有利于功能扩展
  • 使类与类之间产生联系,为多态提供了前提
  1. 格式:class A extends B{}
  • A:子类、派生类、subclass
  • B:父类、超类、superclas
  1. 体现:
  • 子类A继承父类B以后,子类A中就自动获取了父类B中声明的所有的属性和方法。
  • 直接父类、间接父类、自身都有同名属性的情况下,访问时执行就近原则。
  • 方法按重写规定执行。
  • 父类中声明为private的属性或方法,子类继承父类以后,仍然认为获取了父类中私有的结构。只有因为封装性的影响,使得子类不能直接调用父类的结构而已。需要通过setter和getter调用属性。
  • 通过super.属性的方法也访问不到
  • 子类继承父类以后,还可以声明自己特有的属性或方法:实现功能的拓展。
  • 子类必须调用父类的构造器,完成父类的初始化。
  • 实例化过程中会一直调用至Object对象。
  • 注意点:子类A继承自父类B后,如果父类B中声明了带参的构造器,则必须在子类A中声明A的构造函数,且必须在首行通过super(形参列表)调用B的构造函数。否则会编译报错。
  • 原因为如果子类A中不写构造器,其默认构造器为无参构造器,无参构造器中默认会调用B的无参构造器super()。由于父类B中显式地声明了构造器,导致默认的无参构造器不可用,从而会报错。而如果不在首行写super(形参列表)语句,则表明调用的是super(),同样会报错。
  • 解决方案一:父类B中显式声明无参构造器。
  • 解决方案二:子类A中调用父类B中指定的构造器。声明A的构造器方法体内使用super(形参列表)
  1. 规定:
  • 一个父类可以被多个子类继承。
  • 子类直接继承的父类,称为:直接父类。间接继承的父类称为:间接父类。
  • 子类继承父类以后,就获取了直接父类以及所有间接父类中声明的属性和方法。
  • 如果我们没有显式的声明一个类的父类的话,则此类继承于java.lang.Object类。
  • 所有的java类(除java.lang.Object类之外)都直接或间接的继承于java.lang.Object类
  • 所有的java类具有java.lang.Object类声明的功能。
  1. 子类对象的实例化过程
  • 从结果上来看:(继承性)
  • 子类继承父类以后,就获取了父类中声明的属性或方法。
  • 创建子类的对象,在堆空间中,就会加载所有父类中声明的属性。
  • 从过程上来看:
  • 通过子类的构造器创建子类对象时,一定会直接或间接的调用其父类的构造器,进而调用父类的父类的构造器,直到调用了java.lang.Object类中空参的构造器为止。
  • 正因为加载过所有的父类的结构,所以才可以看到内存中有父类中的结构,子类对象才可以考虑进行调用。
  • 虽然创建子类对象时,调用了父类的构造器,但是自始至终就创建过一个对象,即为new的子类对象。

4.3.3 多态性(Polymorphism

  1. 理解:方法和对象的多种形态。实现代码的通用性。
  2. 体现:
  • 方法多态:
  • 重载多态(参数不同,方法体现出多重形态)。
  • 重写多态。
  • 对象多态:
  • 对象的编译类型和运行类型可以不一致,编译类型在定义时确定,不能变化。
  • 对象的运行类型可以是变化的,可以通过getClass()查看运行类型。
  • 常将父类对象作为方法形参,执行方法时根据具体实例化的父/子类对象进行传入。
  1. 使用:虚拟方法调用
  • 子类中定义了与父类同名同参数的方法,在多态情况下,将此时父类的方法称为虚拟方法,父类根据赋给它的不同子类对象,动态调用属于子类的该方法。这样的方法调用在编译期是无法确定的。
  • 编译期只能调用父类中声明的方法,但在运行期,实际执行的是子类重写父类的方法。
  • 不能调用子类中特有的成员。
  • 需要调用子类特有成员时,需要使用向下转型。
  • 总结:编译,看左边;运行,看右边。
  1. 使用前提:
  • 类的继承关系
  • 方法的重写
  1. 对象的多态性,只适用于方法,不适用于属性(编译和运行都看左边)。
  • 使用向下转型声明的父类对象,调用父子类同名的属性时,实际调用的是父类的属性。
  • 属性不具有多态性。
  • 由于父类的属性通常设置为private,所以需要getter方法才能获取到。
  1. instance of操作符:
  • x instanceof A:检验x的运行类是否为类A或其子类的对象,返回值为boolean型。
  • 如果x是类B的对象,而B是A的子类,则仍返回true。
AAaa=newBB();
System.out.println(aainstanceofAA);
System.out.println(aainstanceofBB);
//设BB继承自AA
  • 强制转型后的类型判断:这里均返回true,因为运行的是BB类
  • x instanceof Object:总是返回true。
  • 检验的A类必须与x的类有父子类关系,否则编译会报错。
  1. 造型:对java类型的强制类型转换
  • 子类到父类可以自动类型转换——多态、向上转型
  • 父类到子类必须通过造型()实现——向下转型。
  • 无继承关系的引用类型造型非法,运行报错:ClassCastException
  • 通常首先使用instanceof操作符进行判断后进行造型操作,避免报错。
  1. java的动态绑定机制:
  • 当调用对象的方法时,该方法会和对象的内存地址/运行类型绑定。
  • 当调用对象的属性时,没有动态绑定机制,哪里声明,哪里使用。

4.4 关键字

4.4.1 this

  1. 作用:
  • 方法内部使用时,代表方法所属对象的引用
  • 构造器内部使用时,代表该构造器正在初始化的对象
  1. 使用范围:属性、方法、构造器
  2. 时机:方法内需要调用该方法的对象时,可以使用this。
  • 在任意方法或构造器内,如果使用当前类的成员变量或成员方法可以在其前面添加this,增强程序的阅读性。不过,通常我们都习惯省略this。
  • 当形参与成员变量同名时,如果在方法内或构造器内需要使用成员变量,必须添加this来表明该变量是类的成员变量
  • 使用this访问属性和方法时,如果在本类中未找到,会从父类中查找
  1. this调用构造器
  • 可以在类的构造器中使用this(形参列表)的方式,调用本类中重载的其他的构造器!
  • 明确:构造器中不能通过this(形参列表)的方式调用自身构造器
  • 如果一个类中声明了n个构造器,则最多有n-1个构造器中使用了this(形参列表)
  • this(形参列表)必须声明在类的构造器的首行!
  • 在类的一个构造器中,最多只能声明一个this(形参列表)
  1. 有参的构造器如果没有显式地调用this(),则不会调用无参的构造器。因为如果没写this(),也没写super(0,默认调用的是super()。

4.4.2 package

  1. 作用:package语句作为Java源文件的第一条语句,指明该文件中定义的类所在的包。(若缺省该语句,则指定为无名包)。
  2. 格式:package 顶层包名.子包名
  • 一般:com.公司名.项目名.业务模块名
  1. 使用规范:
  • 包对应于文件系统的目录,package语句中,用.来指明包(目录)的层次
  • 包通常用小写单词标识。通常使用所在公司域名的倒置
  1. 作用:
  • 包帮助管理大型软件系统:将功能相近的类划分到同一个包中。比如:MVC的设计模式
  • 包可以包含类和子包,划分项目层次,便于管理
  • 解决类命名冲突的问题
  • 控制访问权限
  1. 注意点:
  • 同一个包下,不能命名同名的接口、类。
  • 不同的包下,可以命名同名的接口、类。
  1. 常见包:
  • java.lang----包含一些Java语言的核心类,如String、Math、Integer、 System和Thread,提供常用功能
  • java.net----包含执行与网络相关的操作的类和接口。
  • java.io ----包含能提供多种输入/输出功能的类。
  • java.util----包含一些实用工具类,如定义系统特性、接口的集合框架类、使用与日期日历相关的函数。
  • java.text----包含了一些java格式化相关的类
  • java.sql----包含了java进行JDBC数据库编程的相关类/接口
  • java.awt----包含了构成抽象窗口工具集(abstract window toolkits)的多个类,这些类被用来构建和管理应用程序的图形用户界面(GUI)。 B/S C/S

4.4.3 import

  1. 作用:为使用定义在不同包中的Java类,需用import语句来引入指定包层次下所需要的类或全部类(.*)。
  2. 语法格式:import 包名.类名;
  3. 使用细节:
  • 在源文件中使用import显式的导入指定包下的类或接口
  • 声明在包的声明和类的声明之间。
  • 如果需要导入多个类或接口,那么就并列显式多个import语句即可
  • 可以使用java.util.*的方式,一次性导入util包下所有的类或接口。
  • 如果导入的类或接口是java.lang包下的,或者是当前包下的,则可以省略此import语句。
  • 如果在代码中使用不同包下的同名的类。那么就需要使用类的全类名的方式指明调用的是哪个类。
  • 如果已经导入java.a包下的类。那么如果需要使用a包的子包下的类的话,仍然需要导入。
  • import static组合的使用:调用指定类或接口下的静态的属性或方法

4.4.4 super

  1. 用途:子类调用父类的属性、方法、构造器
  • 解决子父子类属性、方法冲突的问题。
  • 在合适的位置调用父类的属性、方法、构造器。
  1. 操作对象:属性、方法、构造器
  2. 调用属性和方法:
  • 子类的方法和构造器中,使用父类的属性和方法时,默认使用了super.的方式调用属性、方法。编程中习惯不写super.
  • super不能调用父类的私有属性。
  • 当子类和父类中定义了同名的属性时,使用super.属性调用的是父类的属性;如果属性没有重名,使用super.属性this.属性作用一样。
  • 在子类中重写的方法中调用父类被重写的方法时,必须使用super.方法的方式。
  • super不能调用父类的私有方法。
  • super调用父类时,不局限于直接父类,有多个上级类有重名方法、属性时,遵循就近原则。
  1. 调用构造器:
  • 可以在子类的构造器中,显式地使用super(形参列表)的方式调用父类的构造器。
  • super(形参列表)必须声明在首行。
  • 由于this(形参列表)也必须出现在首行,所以一个构造器中this(形参列表)super(形参列表)只能二选一,不能同时出现。
  • 在构造器的首行,没有显式的声明this(形参列表)super(形参列表),则默认调用的是父类中空参的构造器:super()
  • 在类的多个构造器中,至少有一个类的构造器中使用了super(形参列表),调用父类中的构造器。
  • 因为所有的对象都是继承来的,都必然具有父类的特征。
  1. 默认的无参构造器默认调用的super()
  • 无论哪个构造器创建子类对象,需要先保证初始化父类,当子类继承父类以后,继承了父类中的属性和方法,因此子类有必要知道父类如何对对象初始化。

4.4.5 static

  1. 设计思想:
  • 属性:在各个对象间共享,不因对象的不同而改变。
  • 方法:方法的调用与对象无关,不需要创建对象就可调用方法。
  1. 修饰范围:
  • 可修饰:属性、方法、代码块、内部类
  • 被static修饰的属性叫:静态属性、静态变量、类变量。
  • 没有被static修饰的属性叫非静态属性、实例变量。
  • 被static修饰的方法叫:静态方法。
  • 不能修饰:局部变量、形参、构造器、外部类
  • 不能修饰构造器的原因:static随着类的加载而加载,根据构造器的加载时机区分static和非static,先于构造器加载的为static,后于构造器加载的为非static
  1. 特点:
  • 属性和方法随着类的加载而加载,与是否创建对象无关。
  • 由于类只会在JVM的内存里加载一次,所以属性会只有一份。
  • 存在于方法区的静态域中。
  • 无论创建多少次对象,使用多少次类,都只有一份
  • 属性和方法优先于对象而存在。
  • 被static修饰的成员,被所有对象所共享。
  • 访问权限允许时,可不创建对象,直接被类调用。

被static修饰的内部类实例化的特点呢?以及与多线程的关系?

  1. 注意点:
  • 静态属性和静态方法可以通过类名.静态属性类名、静态方法名()的方式直接调用,也可以通过对象名.静态属性对象名、静态方法名()的方式调用。
  • 静态方法中,只能调用静态的方法或属性;
  • 同类中的静态方法调用静态方法,可以直接使用方法名();类名、静态方法名(),不能使用this
  • 实例化当前类后,可以通过对象名.静态属性对象名、静态方法名()的方式调用非静态的属性和方法。
  • 非静态方法中,既可以调用非静态的方法或属性,也可以调用静态的方法或属性。
  • 在静态的方法内,不能使用this关键字、super关键字。
  • 也不能通过this.super.的方式调用静态属性、静态方法,只能通过类名.的方式调用。
  • static修饰的方法不能被重写,但可以被继承。
  • 不能被重写:父类、子类的同名、同参静态方法都会被加载。
  • 可以被继承:子类可以直接通过类名.方法名()的方式调用。
  • static修饰内部类,类中的属性、方法、代码块可以是非静态的
  • static可与四种权限修饰符搭配使用,控制可见范围。
  • 设置为private时,只能被类名.静态属性类名.静态方法名()调用,不能被对象的引用调用。
  1. 使用时机:
  • 属性:
  • 属性需要被多个对象共享
  • 常量
  • 方法:
  • 操作静态属性
  • 工具类中的方法。如Math、Collection、Arrays

4.4.6 main

  1. main()说明:
  • main方法的权限修饰符必须是public
  • main方法的修饰符必须是static,因为main方法也是一个内中的方法,需要加载对象的时候执行,而不是创建main方法所在类的对象时候执行。
  • 由于main方法使用static修饰了,所有不能直接访问本类中的非static属性和方法,必须通过实例化创建本类对象的方式进行调用。
  • main方法可以作为与控制台交互的方式
  • 在命令行中执行(运行时,先要生成字节码文件)java 文件名 参数
  • 参数可以带引号,也可以不带,多个参数使用空格分开
  1. 在eclipse工具中执行:
  1. main方法中访问直接访问本类中的静态方法和静态静态属性可以不需要类名.的方式调用。

4.4.7 final

  1. 用途:修饰类、变量(成员变量及局部变量)、方法、内部类
  • 变量分为局部变量和成员变量
  1. final修饰类:不能被继承,提高安全性、可读性。
  • 通常final修饰类后,内部的方法没必要在用final修饰
  • String类、StringBuffer类、System类
  1. final修饰方法:不能被子类重写
  • Object的getClass()
  • 不能修饰构造器
  1. final修饰属性(成员变量):表示常量,且必须在定义时初始化。
  • 赋值的位置可以是类中显式初始化、代码块、构造器内。
  • 搭配static使用时,初始化位置只能是显示初始化和静态代码块内
  1. final修饰局部变量:。
  • final修饰形参时,表示给常量赋值,方法体内只能使用该常量,不能修改该形参。
  • 修饰方法体内局部变量时,称为局部常量。
  1. final可以和static搭配使用,
  • static final:只能用于修饰属性、普通方法,修饰属性时,表示全局常量。

4.4.8 abstract(抽象类与抽象方法)

  1. 修饰结构:类、方法、内部类
  • 不能修饰属性、私有(private)方法、静态(static)方法、final方法、final类
  • 通常用于处理父类方法不缺定时的需求。
  • 先有抽象方法,后有抽象类。
  1. 不可修饰结构:属性、构造器等。
  2. 修饰类:
  • 此类不能实例化
  • 开发中一般提供抽象类的子类,该子类使用多态的方式实例化父类。否则匿名化。
  1. 修饰方法:
  • 抽象方法只有方法名,没有方法体,用;结束
  • 包含抽象方法的类,一定是抽象类;但抽象类不一定包含抽象方法。
  • 抽象类不能被private修饰(因为要对外可见,需要被重写)
  • 若子类重写了父类所有的抽象方法,则该子类可实例化;
  • 若子类没有重写或部分重写了父类的所有抽象方法,则该子类也是一个抽象类,需要使用abstract修饰。
  1. 抽象类的应用:模板方法的设计模式。(见2.6.2)
  2. 匿名类(不一定为抽象类)与匿名对象:共有四种格式
  • 非匿名类非匿名对象:Worker worker = new Worker();
  • 非匿名类匿名对象:new Worker();
  • 匿名类非匿名对象:Person p = new Person(){重写Person的虚拟方法};
  • 注意分号不能省略。
  • Person是一个虚拟类,按理不能实例化,实际上也正是通过方法体中的重写,将Person类变成了其他类,正常来说应该单独定义一个非虚拟具名的类继承自Person,再将方法体写入其中,但正由于没有这样做,不知道这个子类叫什么名字,所以就是匿名类。
  • 在匿名类非匿名对象的方法体中,如果声明了属性a,通过对象引用.p.a的方式访问属性a的值,如果Person类中没有定义过a,则报错,如果Person中定义了a,则返回的是Person类中的a的值。即,匿名类非匿名对象中声明的自有属性访问不到。
  • 如果要能访问到,可采用int p = new Person(){int a = 1;重写Person的虚拟方法}.a;
  • int p = new Person(){int a = 1;重写Person的虚拟方法}.a;改变返回值类型、.a.方法名()可以获取.方法名()的返回值。但是仅限于有返回值类型的方法(含自有方法)。
  • 匿名类匿名对象:new Person(){重写Person的虚拟方法}
  • 注意分号不能省略。

4.5 面向对象补充知识

4.5.1 JavaBean

  1. JavaBean是一种Java语言编写的可重用组件。
  2. 特征:
  • 类是公共的
  • 有一个无参的公共构造器
  • 有属性
  • 属性一般定义为private,有对应的getter、setter
  1. 可以使用JavaBean将功能、处理、值、数据库访问和其他任何可以用Java代码创造的对象进行打包,并且其他的开发者可以通过内部的JSP页面、Servlet、其他JavaBean、applet程序或者应用来使用这些对象。用户可以认为JavaBean提供了一种随时随地的复制和粘贴的功能,而不用关心任何改变。

4.5.2 UML类图

  • Accout:类名
  • +表示public,-表示private,#表示protected
  • 属性:前表示属性名,后表示属性类型
  • 方法:方法的()外面有表示有返回值,后面为返回值类型
  • 方法有下划线表示构造器

4.5.3 MVC设计模式

  1. 内容:常用的设计模式之一,将整个程序分为三个层次:视图模型层,控制器层,与数据模型层。
  2. 优点:这种将程序输入输出、数据处理,以及数据的展示分离开来的设计模式使程序结构变的灵活而且清晰,同时也描述了程序各个对象间的通信方式,降低了程序的耦合性。
  3. 模型层(model):主要处理数据
  • 数据对象封装:model.bean/domain
  • 数据库操作类:model.dao
  • 数据库:model.db
  1. 控制层(controller):处理业务逻辑
  • 应用界面相关:controller.activity
  • 存放fragment:controller.fragment
  • 显示列表的适配器:controller.adapter
  • 服务相关的:controller.service
  • 抽取的基类:controller.base
  1. 视图层(view ):显示数据
  • 相关工具类:view.utils
  • 自定义view:view.ui

4.5.4 Object类的使用

  1. Object类是所有Java类的根父类。
  2. 如果在类的声明中未使用extends关键字指明其父类,则默认直接父类为java.lang.Object类。
  3. Object类中的属性:无
  4. Object类中的方法:
  • equals()
  • 只能比较引用数据类型,作用与==相同,比较是否指向同一对象。
  • File、String、Date及包装类由于重写了equals(),所以比较的时内容是否相同。
  • 重写equals()原则:
  • 对称性:如果x.equals(y)返回是true,那么y.equals(x)也应该返回是true。
  • 自反性:x.equals(x)必须返回是true。
  • 传递性:如果x.equals(y)返回是true,而且y.equals(z)返回是true,那么z.equals(x)也应该返回是true。
  • 一致性:如果x.equals(y)返回是true,只要x和y内容一直不变,不管重复x.equals(y)多少次,返回都是true。
  • 任何情况下,x.equals(null),永远返回是false,null。equals(x)会空指针异常。
  • x.equals(和x不同类型的对象)永远返回是false。
  • toString()
  • 返回值为String,返回类名和它的内存地址——全类名+@+哈希值的十六进制。
  • 全类名:包名+类名
  • File、String、Date及包装类由于重写了toString(),返回"实体内容"信息。
  • 使用String类型的数据与+进行连接操作时,自动调用toString()
  • getClass():获取当前对象所处的类
  • hashCode():返回该对象的哈希值,用于提高哈希表的性能。
  • 两个引用指向同一对象,哈希码值一定一样。
  • 两个引用指向不同对象,哈希码值一般不一样,(极少数情况一样)。
  • 哈希值主要根据地址值生成,但与地址值不同。
  • clone()
  • finalize()
  • 当对象被回收时,系统会自动调用该方法。
  • 当某个对象没有任何引用,则jvm认为该对象是一个垃圾对象。在销毁该对象前,会先调用该方法。
  • 可以通过System.gc()主动出发垃圾回收机制。
  • wait()
  • notify()
  • notifyAll()
  1. Object类只声明了一个空参的构造器
  2. final、finally、finalize的区别?

4.5.5 JUnit单元测试

  1. 步骤:
  • 选中当前工程 - 右键选择:build path - add libraries - JUnit 5.4 - 下一步
  • 创建Java类,进行单元测试。
  • 简便方式:在测试方法上一行写上@Test,然后Ctrl+1修复。
  1. 此时的Java类要求:① 此类是public的  ②此类提供公共的无参的构造器
  2. 此类中声明单元测试方法:方法的权限是public,没有返回值,没有形参。
  3. 此单元测试方法上需要声明注解:@Test,并在单元测试类中导入:import org.junit.Test;
  4. 声明好单元测试方法以后,就可以在方法体内测试相关的代码。
  5. 写完代码以后,左键双击单元测试方法名,右键:run as - JUnit Test
  6. 说明:
  • 如果执行结果没有任何异常:绿条
  • 如果执行结果出现异常:红条

4.6 设计模式

  1. 概念:开发过程中经过大量的实践中总结和理论化之后优选的代码结构、编程风格、 以及解决问题的思考方式。
  2. 分类:
  • 创建型(5种):
  • 结构型(7种):
  • 行为型(11种):

4.6.1 单例模式(Singleton

  1. 定义:采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法
  2. 实现原理:
  • 构造器私有化,防止new
  • 类的内部创建静态实例对象
  • 向外暴露一个静态的公共方法。
  1. 实现方式:
  • 饿汉式:
classSingleton {
// 1.私有化构造器privateSingleton() {}
// 2.内部提供一个当前类的实例// 4.此实例也必须静态化privatestaticSingletonsingle=newSingleton();
// 3.提供公共的静态的方法,返回当前类的对象publicstaticSingletongetInstance() {
returnsingle; 
    }
}
  • 懒汉式:
classSingleton {
// 1.私有化构造器privateSingleton() {}
// 2.内部提供一个当前类的实例// 4.此实例也必须静态化privatestaticSingletonsingle;
// 3.提供公共的静态的方法,返回当前类的对象publicstaticSingletongetInstance() {
if(single==null) {
single=newSingleton();
        }
returnsingle; 
    }
}
  1. 优缺点:
  • 饿汉式:
  • 优点:线程安全。
  • 缺点:对象加载时间过长。全生命周期。
  • 懒汉式:
  • 优点:延迟对象的创建。
  • 缺点线程不安全。
  1. 应用场景:
  • 网站计数器:
  • 应用程序的日志程序:
  • 数据库的连接池
  • 读取配置文件的类:
  • Application:
  • windows系统的任务管理器
  • windows系统的回收站

4.6.2 模板方法设计模式(TemplateMethod

  1. 体现:就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会保留抽象类的行为方式。
  • 也是多态行的一种体现。
  1. 解决的问题:
  • 当功能内部一部分实现是确定的,一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去实现。
  • 换句话说,在软件开发中实现一个算法时,整体步骤很固定、通用,这些步骤已经在父类中写好了。但是某些部分易变,易变部分可以抽象出来,供不同子类实现。
  1. 应用:模板方法设计模式是编程中经常用得到的模式。各个框架、类库中都有他的影子,比如常见的有:
  • 数据库访问的封装
  • Junit单元测试
  • JavaWeb的Servlet中关于doGet/doPost方法调用
  • Hibernate中模板程序
  • Spring中JDBCTemlate、HibernateTemplate等
  1. 实现过程:
  • 定义抽象类
  • 抽象类中写明确定的流程,定义在一个方法(模板方法)内,模板方法调用各个流程
  • 将不确定的内容插入到方法中的合适位置调用
  • 将不确定的内容定义成抽象方法。
  • 子类继承抽象类,重写抽象方法,在方法中完成需要工作的代码。
  • 实例化子类,子类由于继承了父类的方法,通过子类对象的引用调用父类的模板方法的那个方法。

4.6.3 代理模式(Proxy)

  1. 体现:类A实现了接口,实际需要类A去调用(操作)接口时,表面上通过实例化类B去操作,而将A作为一个参数调用。
  2. 步骤:
  • 类A实现接口C
  • 类B实现接口C
  • 类B构造函数传入接口C
  • 在类B内定义检查、校验等方法,并在实现的接口C的方法内调用
  • 创建C 变量名 = new B(new A())
  • 变量名.C的虚拟方法
  1. 应用场景
  • 安全代理
  • 远程代理
  • 延迟加载
  1. 分类:
  • 静态代理(上述描述)
  • 动态代理(JDK自带的静态代理,需要反射实现)

4.6.4 工厂模式(略)

4.7 Interface(接口)

  1. 概述:接口是一组规则的集成,是抽象方法和常量值的集合。
  2. 修饰符:public、缺省。
  • public修饰的接口需要单独建一个文件
  1. 级别:接口是和类并列的一类结构
  2. 语法:
interface接口名{
属性;
抽象方法;
}
class类名extends父类implements接口1, 接口2{
类的自有属性;类的自有方法;必须实现的接口的抽象方法;
}
  • 属性为全局常量,public static final修饰,不写时仍然为public static final,一般不写。
  • 可以省略任意多个修饰符。
  • 声明时必须初始化,因为为final修饰。
  • 接口中的抽象方法可以不用public abstract修饰,不写时仍然为public abstract,一般不写(不写时注意不是缺省,override方法时注意权限修饰符范围,即实现类的重写方法权限修饰符必须为public,重写方法不能写abstract)
  • 可以省略任意多个修饰符。
  • 不能new
  • 接口名有没有abstract修饰不影响
  • JDK7之前:只能定义全局常量和抽象方法:没有方法体
  • JDK8及之后:还可以定义静态方法、默认方法:有方法体
  • 静态方法:static修饰
  • 可以通过接口直接调用并执行方法体。
  • 实现类可以不用重写接口的静态方法
  • 默认方法:default修饰
  • 可以通过类对象来调用
  • 实现类可以不用重写接口的默认方法
  1. 注意点:
  • 接口不能被实例化(不能声明构造器),匿名接口的方式可以new
  • 普通类实现接口,必须实现接口中的所有方法(重写接口中的所有抽象方法)
  • 抽象类实现接口,可以不用实现(重写)接口中的方法
  • 接口可以相互继承,子类接口含有了直接父类、间接父类的所有属性和方法,其实现接口的类必须实现所有方法
  • 接口中属性的访问形式:接口名.属性名或实现接口的类名.属性名对象名.属性名(同虚拟类访问静态属性)
  • 类名.属性名对象名.属性名的方式不能和继承的父类中的属性名冲突,否则会报错,原因为不明确
  • 继承+实现情况下属性名重复会报错
  • 实现+实现情况下属性名重复会报错
  1. 接口也具有多态性
  • 接口名 变量名 = new 实现接口的类名()
  1. 类优先原则:
  • class C extends A implements B中,如果A、B、C中均定义了同名的属性,实例化C后访问该属性时,返回C中的,如果C中没有,则会报错。
  • 如果A中实现了B中的抽象方法,而C中什么也没写,这时根据继承原则,默认C中有了A的方法,从而重写了B中的方法。
  • 两个接口同时定义了同名同参的默认方法:
  • 如果返回值不一致(权限修饰符均一致), 一个类都实现了接口时,会发生接口冲突(报错),无法通过重写两个同名同参不同返回值类型的方法,因为两个接口不知道谁是它的重写。
  • 如果返回值一致,则一个实现类可只写一个重写方法,表示对两个接口方法的重写。
  • 一个接口定义了默认方法,一个父类定义了同名同参数的非抽象方法,不会发生冲突,且类优先,接口中定义的同名同参方法会被忽略。
  • 调用指定接口的方法:接口名.super.默认方法名

4.8 一个小结


内部类

属性

局部变量

方法

构造器

代码块

接口

public

protected

缺省

private

  • public修饰类和接口时,必须单独设置一个文件。


内部类

属性

局部变量

方法

构造器

代码块

接口

static

final

abstract

非private的方法

  • static、final、abstract要写在语句的开头,但与权限修饰符的先后顺序没关系。
  • final和abstract修饰符永远二选一(类、内部类、方法)。
  • final和static永远可搭配(内部类、属性、方法)
  • static和abstract可同时修饰内部类,不可同时修饰方法。
目录
相关文章
|
6月前
|
Java
1.7 面向对象
1.7 面向对象
30 0
|
9月前
真正意义上的面向对象
真正意义上的面向对象
|
9月前
|
C#
C#视频之面向对象
C#视频之面向对象
48 0
|
9月前
面向对象(1)
面向对象(1)
45 0
|
11月前
|
存储 Java 编译器
初步认识面向对象
初步认识面向对象
|
Java
2. 面向对象
面向过程,其实就是面向着具体的每一个步骤和过程,把每一个步骤和过程完成,然后由这些功能方法相互调用,完成需求。
121 1
|
Java
面向对象(三)
面向对象细节内容
70 0
|
存储 Java
面向对象(一)
面向对象(一)
面向对象(一)
|
JavaScript
关于面向对象
一、概述 js对面向对象的实现就是给函数一个原型对象(Prototype),两者结合可以用来表达类。类的实质就是由两个对象组合而成(函数是特殊的对象),它们通过prototype和constructor属性相互引用。
602 0

热门文章

最新文章