Java面向对象编程 学习笔记(2021.07.05~08.16)
面向对象编程
一、什么是面向对象(OOP)
1. 面向对象&面向过程
-
面向过程思想
适合做简单的问题,第一步做什么第二步做什么。。。
-
面向对象思想
处理复杂问题,适合处理多人协作问题。
分类的思维模式,分类完对类进行思考。
对于描述复杂的事物,为了从宏观上把握、从整体上合理分析,我们需要使用面向对象的思路来分析整个系统。但是,具体到微观操作,仍然需要面向过程的思路去处理。
2. 什么是面向对象
本质:以类的方式组织代码,以对象的形式(封装)数据。
思想:抽象
三大特性:封装、继承、多态
从认识论角度考虑是先有对象后有类。对象,是具体的事物。类,是抽象的,是对对象的抽象。从代码运行角度考虑是先有类后有对象。类是对象的模板。
二、回顾方法的定义
public class DemoFunction {
//main 方法
public static void main(String[] args) {
}
/*
修饰符 返回值 方法名(形参...){
//方法体
return 返回值;
}
*/
public static void sayHello(){
System.out.println("Hello!");
}
}
三、回顾方法调用
1. 静态方法(只能使用静态元素)
//静态方法
public static void say(){
System.out.println("学生说话了");
}
//静态方法调用
Student.say();
2. 非静态方法
//非静态方法
public void learning(){
System.out.println("学生学习了");
}
//非静态方法调用
Student student = new Student();
student.learning();
3. 形参和实参
//实参
int i = 0;
i = add(1, 3);//实参(1,3)对应形参(a, b)
System.out.println(i);
public static int add(int a, int b){//形参a,b
return a+b;
}
4. 值传递和引用传递
//值传递方法中参数的传递是值传递,不管方法中如何改动都是不变
public static void valueTurn(){
int a = 1;
System.out.println(a);//1
a(a);
System.out.println(a);//1
}
public static void a(int a){
a = 10;
}
//引用传递:Java中本质还是值传递,此处只需要记得,对象中的值传递后可以直接改变,而非引用类型则不可以,因为对象与内存挂钩
Person person = new Person();
System.out.println(person.name);//null
change(person);
System.out.println(person.name);//xiaowei
public static void change(Person person){
person.name="xiaowei";
}
class Person{
String name;//null
}
5. this关键字
代表自己当前的类。
四、类与对象的创建
1. 类与对象的关系
例如下列表格:
| 类 | 对象 |
|---|---|
| 动物 | 张三家里的狗、李四家的猫、野生的东北虎、等等 |
| 植物 | 自己养的水仙、道路旁边的树、野生的牡丹花、杜鹃花等等 |
类是对象中共同属性抽象出来的描述和概括
对象是类的一个具体情况。
2. 创建类
创建类,类中只有两种东西,属性和方法
public class Animal {
//属性
String type;
String name;
int age;
//方法
//初始化方法也叫构造方法,方法名字为类名,且没有返回值的方法就是构造方法
public Animal(){};//如果有了其他构造方法,也请一定要设立没有参数和方法体的构造方法,这样的构造方法是当你直接new Animal()时默认使用的构造方法
//同,也是构造方法
public Animal(String type,String name,int age){
this.type = type;
this.age = age;
this.name = name;
}
public String getInformation(){
return "这是一只"+type+",它的名字是"+name+",它今年"+age+"岁了";
}
}
3. 初始化类
//初始化方法也叫构造方法,方法名字为类名,且没有返回值的方法就是构造方法
public Animal(){};//如果有了其他构造方法,也请一定要设立没有参数和方法体的构造方法,这样的构造方法是当你直接new Animal()时默认使用的构造方法
//同,也是构造方法
public Animal(String type,String name,int age){
this.type = type;
this.age = age;
this.name = name;
}
4. 实例化类
public static void main(String[] args) { Animal cat1 = new Animal("猫","33",2); Animal dog1 = new Animal(); dog1.name = "旺财"; dog1.type = "狗"; dog1.age = 3; System.out.println(cat1.getInformation()); System.out.println(dog1.getInformation());}
五、构造器详解
即使一个类什么都不写,他也会默认生成一个没有参数和方法体的构造方法(也叫构造器)。
public class Animal {
//属性
String type;
String name;
int age;
//方法
//初始化方法也叫构造方法也叫构造器
public Animal(){};//没有其他的构造器就会默认生成一个这样的构造器
/*
对象实例化时调用相应的构造器
*/
public Animal(String type,String name,int age){
this.type = type;
this.age = age;
this.name = name;
}
public String getInformation(){
return "这是一只"+type+",它的名字是"+name+",它今年"+age+"岁了";
}
}
对象实例化时会调用相应的构造器,根据构造器内容初始化对象。如下:
public static void main(String[] args) {
Animal cat1 = new Animal("猫","33",2);//调用有三个参数的构造器,
Animal dog1 = new Animal();//调用空的构造器,仅仅分配内存,值不做改变
dog1.name = "旺财";
dog1.type = "狗";
dog1.age = 3;
System.out.println(cat1.getInformation());
System.out.println(dog1.getInformation());
}
六、创建对象内存分析
第一步:new出对象,在new对象时,如果这个类没被加载过,则在方法区(位于堆内)加载这个类。
第二步:new对象时现在堆中开辟内存放置新new的对象,并且栈中生成引用变量名,指向堆中相应内存
第三步:修改属性时对相应内存区域做出修改。
第四步:调用堆中对象的方法时,会调用方法区内的类的方法。

七、简单小结类和对象
1. 类与对象
类是抽象出的共同点,是一个模板,对象是类这个模板做出来的实际的物体。
2. 方法
定义&调用方法的代码语法如何使用,是关键。
3. 对应的引用
引用类型:除了基本类型(对象通过引用来操作) &基本类型(八种)
对象是通过引用来操作的:栈指向堆中的地址
4. 属性
字段Field 成员变量
默认初始化:
数字:0 0.0
char:u0000
Boolean:false
引用:null
修饰符 属性类型 属性名 = 属性值;
5. 对象的创建和使用
- 必须要使用new关键字创建对象,构造器 Person xiaowei = new Person();
- 对象的属性 xiaowei.age;
- 对象的方法 xiaowei.sayHello();
6. 类
静态的属性(字段、属性)
动态的行为(方法)
7. 续
封装、继承、多态
八、封装详解
1. 该露的露,该藏的藏
我们程序设计要追求“高内聚,低耦合”。高内聚就是类的内部数据操作细节自己完成,不允许外部干涉;低耦合:仅暴露少量的方法给外部使用。
2. 封装(数据的隐藏)
通常,应禁止直接访问一个对象中数据的实际表示,而应通过操作接口来访问,这称为信息隐藏。
记住这句话就够了:属性私有,get/set
3. 例子
public class Student {
private String name; //姓名
private int Age; //年龄
private char gender; //性别
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return Age;
}
public void setAge(int age) {
if (age>120||age<0){
age = 3;
}
Age = age;
}
public char getGender() {
return gender;
}
public void setGender(char gender) {
if (gender!='男'&&gender!='女'){
gender = '男';
} this.gender = gender;
}
}
4. 封装的作用
- 提高程序安全性保护数据。
- 隐藏代码实现细节。
- 统一接口。
- 系统可维护性增加了。
九、什么是继承
继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模。
extands的意思是“扩展”。子类是父类的扩展。
JAVA中类只有单继承,没有多继承!
继承是类和类之间的一种关系。除此之外,类和类之间的关系还有依赖、组合、聚合等。
继承关系的俩个类,一个为子类(派生类),一个为父类(基类)。子类继承父类,使用关键字extends来表示。子类和父类之间,从意义上讲应该具有"is a"的关系.
object类
super方法重写
如学生继承人,猫继承动物等等。学生是子类,人是父类。。。
JAVA中的所有类都继承Object
例子:
//父类:动物类
public class Animal {
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
//属性
public int age;
//年龄
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String type;
//种类
//方法
//初始化方法也叫构造方法
public Animal(){};
public Animal(int age){
this.age = age;
}
public String getAgeInfo(){
return "它今年"+age+"岁了";
}
}
//子类:宠物类
import com.sun.org.apache.xpath.internal.functions.FuncQname;
public class Pet extends Animal{
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getMastername() {
return mastername;
}
public void setMastername(String mastername) {
this.mastername = mastername;
}
public String getPetInfo(){
return "这只宠物是"+getType()+",它叫"+getName()+",它的主人是"+getMastername()+","+this.getAgeInfo();
}
private String name;//宠物名称
private String mastername;//主人名字}
//应用
public class Application {
public static void main(String[] args) {
Pet cat = new Pet();
cat.setMastername("韦世斌");
cat.setName("小猫");
cat.setType("猫");
cat.setAge(3);
System.out.println(cat.getPetInfo());
}
}
十、Super详解
1. super.字段
相当于引用生成自己的父对象比如super.name就相当于访问了父类的name字段,而不是自己的
2. super()
- super()调用父类的构造方法,必须在构造方法的第一行
- super()必须出现在子类的方法和构造方法中
- 不能同时调用super()和this()两个构造方法
3. 对比this
代表对象的不同
this:本身调用者这个对象
super:代表父类对象的应用
前提:
this:没有继承也可以使用
super:只有在继承条件下才可以使用
构造方法
this():本类的构造
super():父类的构造
4. 例子
public class Father {//父类
private String privateflag = "1";//private私有的,继承也不能使用
protected String protectedflag = "2";//protected仅仅继承可以使用
public String publicflag = "3";//public公共的,任何都可以使用
public Father(){
System.out.println("构造father");
}
protected void dosomething(){
System.out.println("father doing");
}
}
public class Son extends Father{//子类
public String publicflag;
public void dosomething1(){
System.out.println("son doing");
}
public Son(){
super();//默认有父级的构造,即使不写依旧会默认生成
System.out.println("son构造");
}
public void use(){
System.out.println(super.protectedflag);
super.dosomething();
dosomething1();
}
}
public class Application {//应用代码
public static void main(String[] args) {
Father father = new Father();
Son son = new Son();
son.dosomething();
son.dosomething1();
father.dosomething();
son.use();
}
}
十一、方法的重写
1. 重写的特征
- 必须是子类继承父类的关系,子类重写父类的方法!
- 方法名相同。
- 修饰符不能有static,静态方法在编译时就已经生成,和方法重写没有关系。
- 修饰符的范围不能缩小 public>protected>default>private。
- 抛出的一场范围不能扩大 例如:NullPointException<Exception
- 子类的方法和父类的方法名称上一致,但是方法体不同
2. 重写的意义(为什么需要重写)
父类的功能,子类不需要,或者方法上有些不一致不满足的地方需要重写。
3. 例子
public class Father {//父类
public static void test1(){
System.out.println("father test1");
}
public void test2(){//被重写
System.out.println("father test2");
}
}
public class Son extends Father{//子类
public static void test1(){
System.out.println("son test1");
}
@Override
public void test2() {//重写了方法
System.out.println("son test2");
}
}
public class Application {//应用
public static void main(String[] args) {
Son son = new Son();
Father father = new Son();//父类的对象可以使用子类的构造new出来,父类变量名可以指向子类对象
son.test1();
father.test1();//静态方法不受重写影响,并且之和定义时的变量类型相关
son.test2();
father.test2();//被重写了,对象中的方法已经变成了son.test2()
}
}
十二、多态
动态编译:类型:可扩展性
即同一方法可以根据发送对象的不同而采用多种不同的行为方式。
一个对象的实际类型是确定的,但可以指向对象的引用的类型有很多。
多态存在的条件:
- 有继承关系。
- 子类重写父类方法父类引用指向子类对象。
- 注意:多态是方法的多态,属性没有多态性。
多态的表现形式:
- 父类的变量可以指向子类的对象(例子:动物可以指向猫或狗,比如Animal a1 = new Cat(),其中Cat类继承了Animal类,很好理解,因为猫必然是动物,所以动物的变量可以指向类型是猫的对象,反之动物不一定是猫,所以父类的变量可以指向子类的对象)。
- 父类的变量指向子类的对象时,父类的变量不能直接使用子类的独有的方法(在代码中父类的变量指向子类的对象时,父类的对象仍然不知道自己指向的是哪个子类,所以编写代码时不能使用子类独有的方法,这个体现了动态编译的特点,例如:代码中动物类指向猫或狗的对象时,代码尚未编译,Java还不知道动物到底时猫还是狗,所以在编写代码时,这个未完全具体定义的变量就不能使用猫或者狗的特有方法)。
多说无益上代码:
public class Father {//父类
public static void test1(){
System.out.println("father test1");
}
public void test2(){
System.out.println("father test2");
}
}
public class Son extends Father{//子类继承父类
public static void test1(){
System.out.println("son test1");
}
@Override
public void test2() {
System.out.println("son test2");
}
public void iFunction(){//子类独有方法
System.out.println("son iFunction");
}
}
public class Application {
public static void main(String[] args) {
Son son = new Son();
Father father = new Son();//父类的对象可以使用子类的构造new出来,父类变量名可以指向子类对象
son.test1();
father.test1();//静态方法不受重写影响,独属于类,和定义时的变量类型相关
father.iFunction();//这里报错了详见下图,原因就是多态表现形式第二点
son.test2();
father.test2();
}
}
提示强制转换变量类型,这就是因为:在代码中父类的变量指向子类的对象时,父类的对象仍然不知道自己指向的是哪个子类,所以编写代码时不能使用子类独有的方法

十三、instance of 和类型转换
1. instanceof
这个关键字用于比较前后两个变量是否能够转换,可以转换则输出true,不能转换则输出false,变量类型是同级或者不在同一个系中则会报错
System.out.println(X instanceof Y);X实际指向的类型和Y有父子关系则为true,否认则为false。
代码如下:
public class Application {
public static void main(String[] args) {
//System.out.println(X instanceof Y);X实际指向的类型和Y有父子关系则为true,否认则为false
Object object = new Cat();//实际指向Cat
System.out.println(object instanceof Cat);//true
System.out.println(object instanceof Animal);//true
System.out.println(object instanceof Object);//true
System.out.println(object instanceof Dog);//false
System.out.println(object instanceof String);//false
System.out.println("====================================");
Animal animal = new Cat();
System.out.println(animal instanceof Cat);//true
System.out.println(animal instanceof Animal);//true
System.out.println(animal instanceof Object);//true
System.out.println(animal instanceof Dog);//false
//System.out.println(animal instanceof String);//报错,因为相对于Object同级,无法转换
System.out.println("====================================");
Cat cat = new Cat();
System.out.println(cat instanceof Cat);//true
System.out.println(cat instanceof Animal);//true
System.out.println(cat instanceof Object);//true
//System.out.println(cat instanceof Dog);//报错,因为相对于Animal同级,无法转换
//System.out.println(animal instanceof String);//报错,因为相对于Object同级,无法转换
System.out.println("====================================");
}
}
2. 类型转换
在 instanceof返回true后,就可以对类型进行转换。
父类转子类,比如Animal转Cat,需要强制转换如下:
Animal animal = new Animal();Cat cat = (Cat) animal;
子类转父类,直接就可以转换,但是需要损失一些独有的方法:
Cat cat = new Cat();Animal animal = cat;
十四、static详解
示例类:
package com.xiaowei9s.OOP;
import static java.lang.Math.random;
public class Cat extends Animal{
public static int age = 10;//静态属性
public String name;
{//匿名代码块
System.out.println("匿名代码块");
}
static{//静态代码块,只在方法去生成类时执行一次
System.out.println("静态代码块");
}
public Cat() {//构造函数
System.out.println("构造器");
}
public static void go(){//静态方法
System.out.println("go");
}
public void run(){//普通方法
System.out.println("run");
}
}
1. 作为类中属性
public class Application {
public static void main(String[] args) {
System.out.println(Cat.age);//静态变量可以直接通过类调用
Cat cat = new Cat();
cat.name = "33";//普通的变量则不行
}
}
2. 作为静态代码块修饰符
public class Application {
public static void main(String[] args) {
Cat cat = new Cat();
/*
先后输出:
静态代码块
匿名代码块
构造器
*/
}
}
3. 作为静态导入包修饰符
import static java.lang.Math.random;//静态导入包
public class Application {
public static void main(String[] args) {
System.out.println(random());//可以直接使用包中的函数,并且此函数在类第一次加载时静态编译
}
}
4. 作为静态方法
public class Application {
public static void main(String[] args) {
Cat.go();//可以直接通过类调用静态方法
Cat cat = new Cat();
cat.run();//普通的方法则不行,需要new一个对象
}
}
5. 匿名代码块
public class Application {
public static void main(String[] args) {
Cat cat = new Cat();
/*
先后输出:
静态代码块(在创建类时)
匿名代码块(在创建对象时,一般用于初始化对象)
构造器(在创建对象时)
*/
}
}
十五、抽象类
1. 使用格式
在类名前加修饰符abstract,如public abstract class Animal{};那么Animal就变成了抽象类。
在方法名前加修饰符abstract,如public abstract void function();那么function()就变成了抽象方法。
2. 抽象类的特点
- 继承了它的子类必须重写属于该抽象类的抽象方法,除非子类也是抽象类。
- 缺点,必须继承才能使用抽象类,由于Java是单继承,所以具有局限性,只能够继承一个抽象类(解决办法:接口)
- 不能new抽象类,只能靠子类实现。
- 在抽象类内可以写普通方法,抽象方法只能在抽象类中。
- 只是一个约束。
3. 思考题
- 不能用new,抽象类有构造器吗?
- 抽象类存在的意义(为什么使用抽象类)。
十六、接口的定义及实现
普通类:只有具体实现
抽象类:具体实现和规范(抽象方法)都有!
接口:只有规范!自己无法写方法专业的约束!约束和实现分离:面向接口编程
接口就是规范,定义的是一组规则,体现了现实世界中“如果你是...则必须能….”的思想。如果你是天使,则必须能飞。如果你是汽车,则必须能跑。如果你好人,则必须干掉坏人;如果你是坏人,则必须欺负好人。接口的本质是契约,就像我们人间的法律一样。制定好后大家都遵守。
OO的精髓,是对对象的抽象,最能体现这一点的就是接口。为什么我们讨论设计模式都只针对具备了抽象能力的语言(比如c++、java、c#等),就是因为设计模式所研究的,实际上就是如何合理的去抽象。
接口必须有实现类,类可以实现接口。
上代码!
1. 接口的定义
//关键字 interface
public interface UserService{
//接口中的定义都是抽象的,都是默认是public
void add();
void delete();
void update();
void select();
}
public interface TimeService{
int Year = 1999;//默认修饰符 public static final 接口中的变量是常量
void getNowTime();
}
2. 接口的实现
//使用implements实现多个或单个接口
//实现接口的类就必须实现接口中方法
public class UserServiceImpl implements UserService,TimeService{
@Override
void add(){
};
@Override
void delete(){
};
@Override
void update(){
};
@Override
void select(){
};
@Override
void getNowTime(){
};
}
十七、N种内部类
在类中在定义一个类,A类中定义B类,B就是A的内部类。
-
成员内部类
定义:
public class Outer{ private int i; public void out(){ System.out.println("这是外部类的方法"); } public class Inner{ public void in(){ System.out.println("这是内部类的方法"); } public int getI(){//可以获得外部类的私有属性 return i; } } }使用:
Outer outer = new Outer();//必须先实例化外部类 Outer.Inner inner = outer.new Inner(); inner.in(); -
静态内部类
public class Outer{ private int i; public void out(){ System.out.println("这是外部类的方法"); } public static class Inner{//static修饰 public void in(){ System.out.println("这是内部类的方法"); } public int getI(){//静态类不可以获得外部类的非静态私有属性 return i; } } } -
局部内部类
public class Outer{ private int i; public void method(){//在方法内(区域内)的类 class Inner{ public void in(){ System.out.println("这是内部类的方法"); } public int getI(){//静态类不可以获得外部类的非静态私有属性 return i; } } } } -
匿名内部类
public class Outer{
private int i;
public void out(){
System.out.println("这是外部类的方法");
new Inner().in();
}
public class Inner{
public void in(){
System.out.println("这是内部类的方法");
}
public int getI(){//可以获得外部类的私有属性
return i;
}
}
}

浙公网安备 33010602011771号