Java面向对象编程 学习笔记(2021.07.05~08.16)

面向对象编程

一、什么是面向对象(OOP)

1. 面向对象&面向过程

  1. 面向过程思想

    适合做简单的问题,第一步做什么第二步做什么。。。

  2. 面向对象思想

    处理复杂问题,适合处理多人协作问题。

    分类的思维模式,分类完对类进行思考。

对于描述复杂的事物,为了从宏观上把握、从整体上合理分析,我们需要使用面向对象的思路来分析整个系统。但是,具体到微观操作,仍然需要面向过程的思路去处理。

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的对象,并且栈中生成引用变量名,指向堆中相应内存

第三步:修改属性时对相应内存区域做出修改。

第四步:调用堆中对象的方法时,会调用方法区内的类的方法。

image

七、简单小结类和对象

1. 类与对象

类是抽象出的共同点,是一个模板,对象是类这个模板做出来的实际的物体。

2. 方法

定义&调用方法的代码语法如何使用,是关键。

3. 对应的引用

引用类型:除了基本类型(对象通过引用来操作) &基本类型(八种)

对象是通过引用来操作的:栈指向堆中的地址

4. 属性

字段Field 成员变量

默认初始化:

​ 数字:0 0.0

​ char:u0000

​ Boolean:false

​ 引用:null

修饰符 属性类型 属性名 = 属性值;

5. 对象的创建和使用

  1. 必须要使用new关键字创建对象,构造器 Person xiaowei = new Person();
  2. 对象的属性 xiaowei.age;
  3. 对象的方法 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. 封装的作用

  1. 提高程序安全性保护数据。
  2. 隐藏代码实现细节。
  3. 统一接口。
  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()

  1. super()调用父类的构造方法,必须在构造方法的第一行
  2. super()必须出现在子类的方法和构造方法中
  3. 不能同时调用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. 重写的特征

  1. 必须是子类继承父类的关系,子类重写父类的方法!
  2. 方法名相同。
  3. 修饰符不能有static,静态方法在编译时就已经生成,和方法重写没有关系。
  4. 修饰符的范围不能缩小 public>protected>default>private。
  5. 抛出的一场范围不能扩大 例如:NullPointException<Exception
  6. 子类的方法和父类的方法名称上一致,但是方法体不同

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()

    }

}

十二、多态

动态编译:类型:可扩展性

即同一方法可以根据发送对象的不同而采用多种不同的行为方式

一个对象的实际类型是确定的,但可以指向对象的引用的类型有很多。

多态存在的条件:

  1. 有继承关系。
  2. 子类重写父类方法父类引用指向子类对象。
  3. 注意:多态是方法的多态,属性没有多态性。

多态的表现形式:

  1. 父类的变量可以指向子类的对象(例子:动物可以指向猫或狗,比如Animal a1 = new Cat(),其中Cat类继承了Animal类,很好理解,因为猫必然是动物,所以动物的变量可以指向类型是猫的对象,反之动物不一定是猫,所以父类的变量可以指向子类的对象)。
  2. 父类的变量指向子类的对象时,父类的变量不能直接使用子类的独有的方法(在代码中父类的变量指向子类的对象时,父类的对象仍然不知道自己指向的是哪个子类,所以编写代码时不能使用子类独有的方法,这个体现了动态编译的特点,例如:代码中动物类指向猫或狗的对象时,代码尚未编译,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();

    }

}

提示强制转换变量类型,这就是因为:在代码中父类的变量指向子类的对象时,父类的对象仍然不知道自己指向的是哪个子类,所以编写代码时不能使用子类独有的方法

image

十三、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. 抽象类的特点

  1. 继承了它的子类必须重写属于该抽象类的抽象方法,除非子类也是抽象类。
  2. 缺点,必须继承才能使用抽象类,由于Java是单继承,所以具有局限性,只能够继承一个抽象类(解决办法:接口)
  3. 不能new抽象类,只能靠子类实现。
  4. 在抽象类内可以写普通方法,抽象方法只能在抽象类中。
  5. 只是一个约束。

3. 思考题

  1. 不能用new,抽象类有构造器吗?
  2. 抽象类存在的意义(为什么使用抽象类)。

十六、接口的定义及实现

普通类:只有具体实现

抽象类:具体实现和规范(抽象方法)都有!

接口:只有规范!自己无法写方法专业的约束!约束和实现分离:面向接口编程

接口就是规范,定义的是一组规则,体现了现实世界中“如果你是...则必须能….”的思想。如果你是天使,则必须能飞。如果你是汽车,则必须能跑。如果你好人,则必须干掉坏人;如果你是坏人,则必须欺负好人。接口的本质是契约,就像我们人间的法律一样。制定好后大家都遵守。

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的内部类。

  1. 成员内部类

    定义:

    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();
    
  2. 静态内部类

    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;
            }
        }
    }
    
  3. 局部内部类

    public class Outer{
    	private int i;
    	public void method(){//在方法内(区域内)的类
    		class Inner{
                public void in(){
                    System.out.println("这是内部类的方法");
                }
                public int getI(){//静态类不可以获得外部类的非静态私有属性
                    return i;
                }
    		}
    	}
        
    }
    
  4. 匿名内部类

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;
        }
    }
}
posted @ 2021-08-16 21:07  小阴辨  阅读(72)  评论(0)    收藏  举报