Java(1):面向对象
Java复习笔记(一):面向对象
0、怎么理解“万事万物皆对象”
1、在Java语言的范畴内,我们将功能封装到类中,通过类的实例化,来调用具体的功能。
2、Java与前端交互时,将HTML中的标签转换为对象
3、Java与后端交互时,将数据库中的表转换为对象
0、前言:
-
学习Java面向对象的三条主线
- 类和类的成员:属性、方法构、构造器、代码块、内部类
- 面向对象三大特征:封装、继承、多态
- 其他关键字:this、super、package、import、static、final、abstract、interface
1、类和类的成员
1.0、类和对象
-
理解
- 正如生物由不同功能的细胞构成,Java代码由不同功能的类组成。类的作用正是为我们提供各种的功能。
-
语法
修饰符 class 类名{
属性声明;
方法声明;
}
- 说明:
- 类的修饰符只有两种:public、缺省
- 类名:首字母大写
-
类的使用
- 设计类
- 类的实例化
- 类的访问机制:
- 在同一类中,类的方法可以直接访问类的属性(static的方法只能访问static的属性方法)
- 在不同的类中
- 对于 非static的:“对象.属性/方法”来使用;
- static的方法:“类名.方法”
-
对象的使用
-
对于类中非static的属性,每个对象都有自己独立的一套属性值
-
对象的赋值,是直接把地址值给新对象,不new就不会在内存中开辟空间
-
创建对象的内存解析
- Stack(栈,通常指虚拟机栈):存储局部变量
- 方法体内的变量都是局部变量包括
- 基本数据类型
- 对象的引用(这里指的是对象再heap中的首地址)
- 说明:main方法中的变量也是局部变量,因为main()也是一个方法
- Heap(堆):存放对象实例(new的对象、数组等)
- 引用数据类型只能存放两种数据:null和地址值
- Stack(栈,通常指虚拟机栈):存储局部变量
-
对象数组的内存解析
-
-
匿名对象
-
语法
new Person().eat();
-
使用场景
- 一个对象只需要一次方法调用时,可以使用匿名对象
- 作为实参传递给一个方法时,可以使用匿名对象(实质是把地址值传递给形参)
-
注意:每一个new都是不同的对象
-
1.1、属性
-
说明
- 属性,也叫成员变量(成员是相对于类而言的,属性是类的成员),英文field
-
语法
修饰符 数据类型 属性名 = 初始化值;
-
权限修饰符:private、缺省、protected、public
权限修饰符 访问权限 public 同一工程 protected 不同包的子类 缺省 同一个包 private 类内部 -
其他修饰符:static、final
-
变量的分类
成员变量(成员指的是类的成员) | 局部变量(局部指的所在的位置比类中还小一级) | |
---|---|---|
声明的位置 | 类内,方法体外 | 方法的形参、方法的内部、代码块内、构造器内 |
修饰符 | 四种权限修饰符+static、final | 不使用权限修饰符,可以用final |
初始化值 | 有默认的初始化值 | 没有默认的初始化值,必须显式的赋值 形参在调用时再赋值 |
内存加载位置 | 堆空间 或 静态域 | 栈空间 |
- 成员变量又分为
- 实例变量:不用static修饰,随着对象的创建而加载,每个对象有自己的一套。
- 类变量:static修饰,是随着类的加载二加载,多用对象共用一套。
- 属性的默认初始化值
成员变量类型 | 初始值 |
---|---|
byte | 0 |
short | 0 |
int | 0 |
long | 0L |
float | 0.0F |
double | 0.0 |
char | 0或'\u0000'(表现为空) |
boolean | false |
引用类型 | null |
- 属性初始化的顺序
1.2、方法
1、说明
- 方法,也叫成员方法,英文method
2、语法
权限修饰符 [static、final] 返回值类型 方法名(形参列表){
方法体;
return 返回值;
}
-
权限修饰符:四种
-
return的说明
- 作用:结束方法、或、结束方法,并返回某个值
- 注意:一旦return执行,后面的语句就不会被执行了
3、类的分类
- 公共类 外 测试类 、main
- 公共类 里
4、方法的重载
- 概念:同一类中,存在一个以上的同名的方法,但是他们的参数类型和参数个数不同
5、可变个数的形参
- 概念:允许直接定义能和多个实参相匹配的形参
- 格式:方法名(参数类型名...参数名)
- 实质:相当于数组,使用方法和数组下标一样,也因此形参中String[] books和String...books是不能共存的
- 应用场景:
- 数据库中,where后面不确定有多少个筛选条件
6、方法参数的值传递机制★★★★★
-
概念
- 形参:方法声明时的参数
- 形参:方法调用时实际传递给形参的参数
-
Java实参怎么传递给形参呢?---只有一种,就是值传递机制(就是把实际参数的副本传递给方法,参数本身不受影响)
- 形参是基本数据类型:把实参的“数据值”传递给形参
- 形参是引用数据类型:把实参的“地址值”传递给形参
-
画内存解析图解题★★★★★
-
例题一:基本数据类型的参数传递
public class eg1 { public static void main(String[] args) { int x = 5; System.out.println("修改前x= " + x); change(x); System.out.println("修改后x= " + x); } public static void change(int x) { System.out.println("change修改前x= " + x); x = 3; System.out.println("change修改后x= " + x); } } /* 修改前x= 5 change修改前x= 5 change修改后x= 3 修改后x= 5 说明:change方法只是得到了x的值5,而不是得到了x */
-
例题二:引用数据类型的参数传递
public class eg2 { public static void main(String[] args) { Person p = new Person(); p.age = 5; System.out.println("修改前age= " + p.age); change(p); System.out.println("修改后age= " + p.age); } public static void change(Person p) { System.out.println("change修改前age= " + p.age); p.age = 3; System.out.println("change修改后age= " + p.age); } } class Person{ int age; } /* 修改前age= 5 change修改前age= 5 change修改后age= 3 修改后age= 3 说明:change方法得到了p的地址值,也指向了heap中的p,因此实际修改了age 结论:要想修改、交换,就要传递引用类型变量 */
-
7、方法的递归
- 递归,构成了隐式的循环
- 使用场景和例题
- 计算1到100自然数的和
- 求n!
- 台阶问题
- 汉诺塔
- 快排
1.3、构造器
-
作用
- 创建对象(无参)
- 创建对象并初始化(带参)
-
语法
权限修饰符 类名(参数列表){ } //权限和类保持一致
-
注意
- 每个类都至少有一个构造器
- 一旦显示定义了构造器,系统就不再提供默认构造器
- 可以创建多个重载的构造器
- 子类不能继承父类的构造器
1.4、代码块
-
作用:对类或对象进行初始化
-
分类:
-
静态代码块(用static修饰)
- 内部可以有输出语句
- 随着类的加载而执行,只执行一次
-
非静态代码块
- 内部可以有输出语句
- 每次创建对象,都会执行一次
-
两者特点和对比
静态代码块 非静态代码块 可以有输出语句 可以有输出语句 可以对类的属性,类的声明做初始化 可以对类的属性,类的声明做初始化 不能调用非静态的 静态非静态都可以调用 优先于非静态代码块执行 随着类的加载而加载,且只执行一次 每创建一个对象,就执行一次,优先于构造器 -
顺序:静态代码块>非静态代码块>构造器
-
-
使用:开发中的使用一般是为了初始化,但是用的较少。源码中出现要看懂
1.5、内部类
1.6、属性赋值的顺序
①默认初始化
②显示初始化==代码块初始化
③构造器初始化
④通过对象.属性/方法的形式赋值
- 后执行的会覆盖先执行的操作
1.7、JavaBean
- JavaBean是java语言写成的可重用组件
- JavaBean是一个类,要符合下列特征
- 类是公共的
- 有一个无参的公共的构造器
- 有属性和对象的get、set方法
1.8、UML类图
2、面向对象三大特征
2.1、封装
-
封装的引入
- 使用者如果可以直接操作类内部定义的属性,会导致:数据错误混乱、安全性问题--->把属性声明为private,再体统public的get、set方法实现对属性的操作。
- 当然,private还可以用在方法(仅在类的内部使用这个方法)和构造器(单例模式)中。
2.2、继承
1、继承的引入
- 多个类中具有相同的属性和行为,把这些内容抽取到单独的一个类中,其他的类只需要继承这个类,就能得到这些属性和行为。
- 语法:
class Subclass extends Supclass{}
- java只支持单继承
2、重写(override/overwrite)
- 重写后的方法会覆盖父类中的方法,也因此,子类更具体、更强大
- 重写的要求
- 两同一不同:方法名相同、参数列表相同;方法体不同
- 重写的方法
返回值小于等于
被重写的 - 重写的方法
访问权限大于等于
被重写的 - 子类不能重写父类中private的方法(不可见)
- 子类重写方法抛出的
异常小于等于
父类被重写的方法的异常
2.3、多态
1、多态性的体现
- 父类的引用指向子类的对象// 子类的对象赋给父类的引用//
Person p = new Man()
2、多态的应用
- 方法形参要的是父类类型,我们可以传递给他子类的对象
3、虚拟方法调用★★★★★
- 含义:子类重写了父类的方法,多态情况下,父类的方法就叫
虚拟方法
。父类根据赋给他的 不同子类的对象,动态的调用属于子类的该方法。这样的方法调用在编译器是无法确定的,因此称为动态绑定
- 用法
- 编译时,只能调用引用类型的变量中(即父类中)有的方法
- 运行时,调用实际new的对象的类中重写的方法
- 应用示例:连接数据库时
4、instanceof
-
引入:有了多态以后,内存中有了子类的属性和方法,但是我们声明的父类类型,因此调用不了子类特有的属性和方法。怎么调用子类特有的属性方法呢---->强制类型转换为子类类型再调用,这时就要判断是不是这种类型,再进行转换。
-
自动类型提升(从子类到父类,自动进行)和强制类型转换(从父类到子类,instanceof判断再进行)
-
区分
-
对于属性来说,编译和运行都看左边。(也就是说,属性不会被继承锁覆盖,无论如何都是自己的属性)
-
对于方法来说,编译看左边(只调用父类的方法保证编译器不报错),运行看右边(实际执行的是new的对象的类的方法)。
-
怎么证明多态是运行时行为★★★★★
class Animal { protected void eat() { System.out.println("animal eat food"); } } class Cat extends Animal { protected void eat() { System.out.println("cat eat fish"); } } class Dog extends Animal { public void eat() { System.out.println("Dog eat bone"); } } class Sheep extends Animal { public void eat() { System.out.println("Sheep eat grass"); } } public class InterviewTest { public static Animal getInstance(int key) { switch (key) { case 0: return new Cat (); case 1: return new Dog (); default: return new Sheep (); } } public static void main(String[] args) { int key = new Random().nextInt(3); System.out.println(key); Animal animal = getInstance(key); animal.eat(); } }
-
多态笔试题
public class InterviewTest1 { public static void main(String[] args) { Base1 base = new Sub1(); base.add(1, 2, 3); Sub1 s = (Sub1)base; s.add(1,2,3); } } class Base1 { public void add(int a, int... arr) { System.out.println("base1"); } } class Sub1 extends Base1 { 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"); } }
-
5、例题
3、其他关键字
3.1、this
- 含义:表示当前对象
- 调用谁:this可以调用
属性、方法、构造器
- 在哪里调用
- 在方法或构造器内,如果使用当前类的成员变量或成员方法,可以在其前面加this。但通常省略。
- 当形参和成员变量同名,在成员变量前加this以示区分
- 用this来访问属性和方法时,本类中没有,就会去父类中找
- 在构造器中调用时的几点说明
- 构造器中可以用
this(形参列表)
,调用本类中其他重载的构造器 - 不能调用自身、不能成环、n个构造器最低多有n-1个用
this(形参列表)
- 构造器中可以用
3.2、super
- 含义:父类中的
- 调用谁:super可以调用
属性、方法构、造器
- this和super的对比:this是调用的本类中的,二super是调用父类中的
3.3、package
-
引入:包主要用来对类和接口进行分类。当开发 Java 程序时,可能编写成百上千的类,因此很有必要对类和接口进行分类。
-
格式:
package 顶级报名.子包名;
,分别对应目录的层次 -
位置:第一行
-
了解一下JDK中主要的包
包 说明 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
3.4、import
-
引入:在 Java 中,如果给出一个完整的限定名,包括包名、类名,那么 Java 编译器就可以很容易地定位到源代码或者类。import 语句就是用来提供一个合理的路径,使得编译器可以找到某个类
-
使用说明
-
java.util.*----一次导入util下的所以类和接口
-
如果导入的类或接口是java.lang包下,或当前包下,可以省略导包
-
代码中包含不同包下的同名类,需要用全类名
-
如果已经导入导入了java.a包下的类,如果要使用a包的子包中的类,还是要导包
-
import static
组合使用:调用指定类或接口下的静态的属性和方法import static java.lang.System.* out.println("hello"); 等价于:System.out.println();
-
3.5、static
-
static的引入
- 当我们希望:不管是否产生对象,不管有多少对象,某些特定的数据在内存中只有一份。
- 这时,我们就可以引入static关键字
- static关键字可以修饰:属性、方法、代码块、内部类
- 修饰后的成员的特点:
- 随着类的加载而加载,因此:①类只加载一次,静态变量也加载一次,加载在方法区的静态域中②优先于对象存在③可以不创建对象,而直接用类来调用
- 被所以对象共享
-
static修饰属性
-
static修饰属性---->多个对象共享这个静态变量,如果一个对象修改了这个静态变量,后续的对象再调用,就是修改过后的。
-
实例变量和静态变量
变量类型 特点 实例变量 每个对象都有独立的一套属性 静态变量 公用一个静态变量
-
-
static修饰方法
- 可以不创建对象直接用类调用方法
- static修饰的方法不能被重写
-
应用场景
-
static属性
- 不会随着对象的变化而变化的属性,可以声明为static的
- 具体的场景有:自动赋值、自动生成等字眼(这时就可在构造器加入:
静态变量++
注:每个构造器都要加)
-
static类
- 操作静态属性的方法,通常是静态方法
- 工具类中的方法,习惯上声明为static的(Math、Arrays、Collections)
-
单例模式(面试题):某个类只存在一个对象实例
-
饿汉式
class Singleton(){ //1.外部不能创建---私有化类的构造器 private Singleton(){} //2.内部帮你创建---内部提供当前类的实例 private static Singleton single = new Singleton(); //3.给外部提供得到的方法---提供公共的静态方法,返回当前类的对象 public static Singleton getInstance(){ return single;//4.这里用到了single,所以必须是静态的,回去改正 } }
-
懒汉式
class Singleton(){ //1.外部不能创建---私有化类的构造器 private Singleton(){} //2.内部帮你创建---内部提供当前类的实例 private static Singleton single; //3.给外部提供得到的方法---提供公共的静态方法,返回当前类的对象 public static Singleton getInstance(){//4.这里用到了single,所以必须是静态的,回去改正 if(single == null){ single = new Singleton; } return single; } }
-
两者对比
饿汉式 懒汉式 好处 线程安全 延迟创建对象 坏处 对象加载的时间过长 目前的写法线程不安全(多线程时修改) -
使用场景
- 网站计数器、数据库连接池、任务管理器、回收站
-
-
3.6、final
- final类
- 不能被继承(绝后。。。)
- 举例:String类、System类、StringBuffer类
- 为什么要声明为final:因为系统把该有的功能都提供了,不用我们自己搞新花样啦!
- final方法
- 不能被重写(自己写方法一般不加final)
- 举例:Object类中的getClass()
- 为什么:只让我们用这个方法做这一件事
- final
- final+变量==常量
- 修饰属性(成员变量):必须显式赋值/代码块中赋值/每个构造器中都初始化
- 修饰局部变量:使用final修饰形参时,表示形参是常量,调用方法时赋常量给形参,只能在方法体内使用,而且不能再修改
- 举例:PI
- 为什么:不能修改这个数,也没必要改
- final+变量==常量
- static final一起使用
- 修饰属性:全局常量(接口中的属性都是这样的)
- 修饰方法:很少用到
3.7、abstract★★★★★
-
引入:随着继承层析的增加,子类越来越具体,父类越来越一般、通用、抽象。有时一个父类特别抽象,以至于没有具体的实例,这样的类就叫抽象类。抽象类就像作文模板,我们只需要在可变的部分修修补补就好了。
-
abstract修饰类
- 抽象类不能实例化。因此,我们都要提供他的子类,让子类实例化(否则抽象类有意义)。
- 抽象类中仍然有构造器。便于子类实例化的时候使用
- 包含抽象方法的类,一定是抽象类(因为有了抽象方法,就要保证这个抽象方法不能被调用)。反之,抽象类里可以没有抽象方法。
-
abstract修饰方法
- 格式:加abstract关键字,没有方法体,并且分号结尾
- 子类重写了所有父类中的所有抽象方法,才可以实例化。否则,还是抽象类,要加abstract。
-
应用场景举例
- 计算计划人图形的面积(因为不知道是具体什么图形,计算方法就不一样)
-
注意
- abstract不能修饰私有方法:因为子类不可见
- abstract不能修饰静态方法
- abstract不能修饰final的类或方法
-
抽象类的匿名子类(只用一次,图省事)(见到要认识)
//Person类是抽象的 //创建匿名子类的对象 Person p = new Person(){//并不是new的Person的对象,而是子类的对象 @Override public void eat(){ } } //创建匿名子类的匿名对象 method(new Person(){ @Override public void eat(){ } })
3.8、interface★★★★★
-
引入:Java不支持多继承,但是我们需要这个功能,所以用接口interface来实现多重继承的效果
-
接口的本质:标准、规范。接口是
抽象方法
和全局常量
的集合 -
接口的特点
- 和类是并列的关系
- 接口中的成员变量
默认
是public static final
修饰的,即使省略了,也还是存在。因此接口中的成员变量是全局常量
- 接口中的方法
默认
是public abstract
修饰的,即使省略了,也还是存在。因此接口中的方法是抽象方法
- 接口中没有构造器,因此不能实例化。只能通过类实现接口实现所有的抽象方法,在实例化
-
接口和抽象类的对比(面试题)