Java学习笔记——面向对象编程(核心)


面向对象是相对于面向过程而言的。面向过程,强调的是功能行为。面向对象,将功能封装进对象,强调具备了功能的对象。
面向对象更加强调运用人类在日常的思维逻辑中采用的思想方法与原则,如抽象、分类、继承、聚合、多态等。

1 类和对象

类(class)和对象(object)是面向对象的核心概念。
类是对一类事物描述,是抽象的、概念上的定义。
对象是实际存在的某类事物的某个个体,因而也称实例(instance)。

类与类的关系

  • 关联
  • 继承
  • 聚合
    • 聚集
    • 组合

1.1 类成员

  • Field 字段 = 属性 = 成员变量

  • Method = (成员)方法 = 函数

  • Constructor 构造器

  • 初始化块(代码块)
    作用:对Java对象进行初始化

    • 属性赋值操作的顺序:
      (1) 默认的初始化
      (2) 声明时显式的初始化 或 代码块初始化 (按照声明或代码块顺序执行)
      (3) 构造器中
      (4) 通过方法对对象的相应属性进行修改
    • 静态代码块(用static修饰)
      随着类的加载(第一次创建类的对象或者调用类的静态方法时,等)而加载,只加载一次。

    类的加载顺序:
    (1) 父类静态代码块(包括静态初始化块,静态属性,但不包括静态方法)
    (2) 子类静态代码块(包括静态初始化块,静态属性,但不包括静态方法 )
    (3) 父类非静态代码块( 包括非静态初始化块,非静态属性 )
    (4) 父类构造函数
    (5) 子类非静态代码块 ( 包括非静态初始化块,非静态属性 )
    (6) 子类构造函数

  • 内部类

    • 成员内部类(声明在类内部且方法外的)
      同时拥有类和类成员的特征。
      • 作为类的成员
        (1) 可以用final、static、private、protected修饰
        (2) 可以调用外部类的属性、方法
      • 作为类
        可以声明为abstract类,因此可以被其它的内部类继承。
        注意:非static的内部类中的成员不能声明为static的。
    • 局部内部类(声明在类的方法里)
    • 匿名内部类
      匿名内部类不能定义任何静态成员、方法和类,只能创建匿名内部类的一个实例。一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类。
    new 父类构造器(实参列表) | 实现接口(){
    	//匿名内部类的类体部分
    }
    

定义内部类:

class Person{
	// 成员内部类 (非static的)
	class Bird{}
	//成员内部类 (静态内部类)
	static class Dog{}
}

创建内部类的对象:

//创建静态内部类的对象:可以直接通过外部类调用静态内部类的构造器
Person.Dog d = new Person.Dog();
//创建非静态的内部类的对象:必须先创建外部类的对象,通过外部类的对象调用内部类的构造器
Person p = new Person();
Person.Bird b = p.new Bird();

区分内部类、外部类的变量:

public class A{
       private int s = 111;
       public class B {
		private int s = 222;
		public void mb(int s) {
	        System.out.println(s);              // 局部变量s
	        System.out.println(this.s);      // 内部类对象的属性s
	        System.out.println(A.this.s);   // 外层类对象属性s
        }
	}
}

1.2 类的创建和使用

public class TestOOP {
	public static void main(String[] args) {
		// 2.  创建类的实例(对象)
		Animal nAnimal = new Animal("猴子", 1);
		// 调用类的属性或方法
		nAnimal.eat();
		System.out.println(nAnimal.getName());
	}
}

// 1. 设计并定义类
class Animal{
	// 属性
	private String name;
	public int age;
	
	// 构造器
	public Animal() {}
	public Animal(String name, int age) {
		this.name = name;
		this.age = age;
	}
	
	// 方法
	public void eat() {
		System.out.println(name + "进食");
	}
	
	public void setName(String name) {
		this.name = name;
	}
	
	public String getName() {
		return this.name;
	}
	// 代码块
	{
		System.out.println("??");
	}
}

1.3 权限修饰符

public、protected、private置于类的成员定义前,用来限定对象对该类成员的访问权限。

修饰符类内部同一个包子类任何地方
privateYes
(缺省)YesYes
protectedYesYesYes
publicYesYesYesYes

总结:

  • 在同一个包内,只有private的方法不能在其他类中访问。
  • 在不同包,public的方法可以被其他类访问,protected只能被子类访问。

对于class的权限修饰只可以用public和default(缺省)。

  • public类可以在任意地方被访问。
  • default类只可以被同一个包内部的类访问。

1.4 小知识

  • 成员变量有默认初始化值,局部变量没有,必须显式初始化。
  • 方法的重载 (overload)
    方法名相同,参数类型不同(个数,类型,顺序)
  • 可变个数形参 (jdk5.0之后)
    public void test(int i, String...strs){
    	// 可将strs看做数组
    }
    

2 面向对象三大特征

2.1 封装 (Encapsulation)

  • 将类的属性私有化 private
  • 提供公共的方法 (public) 来实现属性的调用

2.2 继承 (Inheritance)

public class Person {
      public String name;
      public int age;
      public Date birthDate;
      public String getInfo() {...}
}

public class Student extends Person{
      public String school;
}
2.2.1 定义

多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。
此处的多个类称为子类,单独的这个类称为父类(基类或超类)。可以理解为:“子类 is a 父类”。语法:

class Subclass extends Superclass{ }
2.2.2 作用
  • 继承的出现提高了代码的复用性。
  • 继承的出现让类与类之间产生了关系,提供了多态的前提。
2.2.3 注意
  • Java只支持单继承,不允许多重继承。
    • 即一个子类只能有一个父类。
    • 但一个父类可以派生出多个子类
2.2.4 方法的重写(override)

在子类中可以根据需要对从父类中继承来的方法进行改造。
在程序执行时,子类的方法将覆盖父类的方法。

  • 要求
    • 重写方法必须和被重写方法具有相同的方法名称、参数列表和返回值类型。
    • 重写方法不能使用比被重写方法更严格的访问权限。
    • 重写和被重写的方法须同时为static的,或同时为非static的。
    • 子类方法抛出的异常不能大于父类被重写方法的异常。
  • 注意
    如果父类的一个方法定义成private访问权限,在子类中有相同的方法,则不叫重写,因为子类无法访问到
package mytest;

public class A{
	private void privateFunc() {
		System.out.println("A privateFunc");
	}
	
	void defaultFunc() {
		System.out.println("A defaultFunc");
	}
	
	protected void protectedFunc() {
		System.out.println("A protectedFunc");
	}
	
	public void publicFunc() {
		System.out.println("A publicFunc");
	}
}
package mytest;

public class B extends A{
	// 不是重写,而是定义了另外一个函数,重写的前提是有访问权限
	private void privateFunc() {
		System.out.println("B privateFunc");
	}
	
	// 重写
	void defaultFunc() {
		System.out.println("B defaultFunc");
	}
	
	// 重写
	protected void protectedFunc() {
		System.out.println("B protectedFunc");
	}
	
	// 重写
	public void publicFunc() {
		System.out.println("B publicFunc");
	}
}
package mytest;

public class Test {
	public static void main(String[] args) {
		A a = new B();
		B b = new B();
//		a.privateFunc(); // 只能在类内部调用
		a.defaultFunc();
		a.protectedFunc();
		a.publicFunc();
		
//		b.privateFunc(); // 只能在类内部调用
		b.defaultFunc();
		b.protectedFunc();
		b.publicFunc();
	}
}

输出:

B defaultFunc
B protectedFunc
B publicFunc
B defaultFunc
B protectedFunc
B publicFunc

2.3 多态 (Polymorphism)

体现:

  • 方法的重载(overload)和重写(override)。
  • 对象的多态性 ——可以直接应用在抽象类和接口上。
    • 需要存在继承或者实现关系
    • 要有覆盖操作

Java引用变量有两个类型:编译时类型和运行时类型。编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。
若编译时类型和运行时类型不一致,就出现多态.

子类可看做是特殊的父类,所以父类类型的引用可以指向子类的对象:向上转型(upcasting)。

一个引用类型变量如果声明为父类的类型,但实际引用的是子类对象,那么该变量就不能再访问子类中添加的属性和方法。

// B继承A
A a = new B(); // 向上转型
a.defaultFunc(); // 虚拟方法调用(Virtual Method Invocation)
//a.bfunc(); // 无法调用子类中添加的方法
((B)a).bFunc(); // 强制类型转换(向下转型)
  • 个人总结
    • 继承或实现(implement)
    • 重写(override)
    • 子类对象赋给父类的指针
    • 编译时类型(声明的类型,决定能调用哪些方法)和运行时类型(实际赋值的类型,决定调用的是哪个类的方法)
    • 即使运行时为子类类型,编译时也不能通过父类的指针访问子类中有但父类中没有的方法,可通过强制类型转换将其转为子类类型(先用instanceof判断其类型),然后就能访问子类中的方法

3 其他关键字

this

它在方法内部使用,表示这个方法所属对象的引用
它在构造器内部使用,表示该构造器正在初始化的对象的引用
可以调用属性、方法、构造器(构造器相互调用,使用this()必须放在构造器的首行)。

super

super可用于访问父类中定义的属性、成员方法,在子类构造方法中调用父类的构造器。
注意:

  • 尤其当子父类出现同名成员时,可以用super进行区分
  • super的追溯不仅限于直接父类
  • super和this的用法相像,this代表本类对象的引用,super代表父类的内存空间的标识
  • 当构造器中,不显式的调用“this(形参列表)”或“super(形参列表)”其中任何一个,默认调用的是父类空参的构造器!
  • 设计一个类时,尽量要提供一个空参的构造器!(子类的构造器会默认调用super())

package

包帮助管理大型软件系统:将语义近似的类组织到包中;解决类命名冲突的问题。
package语句作为Java源文件的第一条语句,指明该文件中定义的类所在的包。(若缺省该语句,则指定为无名包)。
格式为:

package 顶层包名.子包名

import

import  包名[.子包名…]. <类名 |*>
  • 若引入的包为:java.lang,则编译器默认可获取此包下的类,不需要再显示声明。

  • 可以使用import lee.* ;语句,表明导入lee包下的所有类。而lee包下sub子包内的类则不会被导入。

    • JDK 1.5加入import static语句,表示导入指定类的static属性或方法。
      import static java.lang.System.out
      import static java.lang.System.*
      

static

修饰属性(类变量)、方法(类方法)、初始化块(代码块)、内部类。
被修饰后的成员具备以下特点:

  • 随着类的加载而加载
  • 优先于对象存在
  • 被所有对象所共享
  • 访问权限允许时,可不创建对象,直接被类调用
类变量(class Variable)

类变量存在于静态域中。

类方法(class Method)
  • 没有对象的实例时,可以用 类名.方法名() 的形式访问由static标记的类方法。
  • 在static方法内部只能访问类的static属性,不能访问类的非static属性。
  • static方法内不可以有this或super关键字

static方法即使被重写,也不能通过父类的指针(多态)访问到子类重写的static方法。

A a1 = new B();
a1.staticFunc(); // A staticFunc
((B)a1).staticFunc(); // B staticFunc

单例模式
单例模式的几种实现方式#java,简单易懂

final

修饰类、属性和方法。

  • final标记的类不能被继承
    String类、System类、StringBuffer类
  • final标记的方法不能被子类重写
    Object类中的getClass()。
  • final标记的变量(成员变量或局部变量)即称为常量。名称大写(规范),且只能被赋值一次。
    final标记的成员变量必须在声明的同时或在每个构造方法中或代码块中显式赋值,然后才能使用,否则编译不通过。

注意:final修饰对象时,其引用不能改变,但对象内部的属性若没有用final修饰,则可以被修改。

4 Object类

  • Object类是所有Java类的根父类
  • 如果在类的声明中未使用extends关键字指明其父类,则默认父类为Object类

4.1 主要方法

  • public Object()构造方法
  • public boolean equals(Object obj)
    比较是否指向同一个对象。对类File、String、Date及包装类(Wrapper Class)来说,是比较类型及内容而不考虑引用的是否是同一个对象,因为在这些类中重写了Object类的equals()方法。
    与“==”区别:
    • 基本类型比较值: 只要两个变量的值相等,即为true
    • 引用类型比较引用(是否指向同一个对象): 只有指向同一个对象时,==才返回true
    String str1 = new String("hello");
    String str2 = new String("hello");
    System.out.println(str1 == str2);//false
    System.out.println(str1.equals(str2));//true
    System.out.println(“hello” == new java.sql.Date()); //编译不通
    
    字符串常量池:
    Person p1 = new Person();
    p1.name = "atguigu";
    
    Person p2 = new Person();
    p2.name = "atguigu";
    
    System.out.println(p1.name.equals(p2.name));//true
    System.out.println(p1.name == p2.name);//true
    
    String string3 = "string";
    String string4 = "string";
    System.out.println(string3 == string4);//true
    System.out.println(string3.equals(string4));//true
    
  • public int hashCode()
  • public String toString()
    public String toString() {
    	return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }
    
    像String类、包装类、File类、Date类等,已经实现了Object类中toString()方法的重写。

5 包装类(Wrapper)

针对八种基本定义相应的引用类型—包装类(封装类)

基本数据类型包装类
booleanBoolean
byteByte
shortShort
intInteger
longLong
charCharacter
floatFloat
doubleDouble

5.1 装箱

基本数据类型包装成包装类的实例。

  • 通过包装类的构造器实现
    int i = 50;
    Integer t = new Integer(i);
    
  • 还可以通过字符串参数构造包装类对象
    Float f = new Float(4.56);
    Long l = new Long(“asdf”);  //NumberFormatException
    

5.2 拆箱

获得包装类对象中包装的基本类型变量。
调用包装类的.xxxValue()方法

Boolean boolean1 = true;
boolean b = boolean1.booleanValue();

5.3 字符串和基本数据类型之间的转换

  • 字符串转换成基本数据类型
    // 通过包装类的构造器实现
    int i = new Integer(12);
    // 通过包装类的parseXxx(String s)静态方法
    Float f = Float.parseFloat(12.1);
    
  • 基本数据类型转换成字符串
    // 调用字符串重载的valueOf()方法
    String fstr = String.valueOf(2.34f);
    // 调用包装类的toString()方法
    String s1 = Integer.toString(314);
    // 更直接的方式
    String intStr = 5 + “”
    

JDK1.5之后,支持自动装箱,自动拆箱。但类型必须匹配。

6 抽象类 (abstract class)

随着继承层次中一个个新子类的定义,类变得越来越具体,而父类则更一般,更通用。类的设计应该保证父类和子类能够共享特征。有时将一个父类设计得非常抽象,以至于它没有具体的实例,这样的类叫做抽象类。

  • 用abstract关键字来修饰一个类时,这个类叫做抽象类
  • 用abstract来修饰一个方法时,该方法叫做抽象方法
    抽象方法:只有方法的声明,没有方法的实现。以分号结束:
    abstract int abstractMethod(int a);
    

含有抽象方法的类必须被声明为抽象类。(即抽象类中才有的抽象方法)

抽象类不能被实例化。抽象类是用来被继承的,抽象类的子类必须重写父类的抽象方法,并提供方法体。若没有重写全部的抽象方法,仍为抽象类。

不能用abstract修饰属性、构造器、私有方法、静态方法、final的方法。
总结:abstract修饰的方法必须能够被子类重写。
模板方法设计模式(TemplateMethod)

7 接口 (interface)

有时必须从几个类中派生出一个子类,继承它们所有的属性和方法。但是,Java不支持多重继承。有了接口,就可以得到多重继承的效果。
接口(interface)是抽象方法和常量值的定义的集合。

class SubClass implements InterfaceA{ }
  • 接口中的所有成员变量都默认是由public static final修饰的。

  • 接口中的所有方法都默认是由public abstract修饰的。

  • 接口没有构造器。

  • 接口采用多继承机制。

  • 实现接口的类中必须提供接口中所有方法的具体实现内容,方可实例化。否则,仍为抽象类。

  • 接口的主要用途就是被实现类实现。(面向接口编程)

  • 与继承关系类似,接口与实现类之间存在多态性

public interface Runner {
	int ID = 1;
	void start();
	public void run();
	void stop();
}

实现接口的匿名类对象:

Runner r = new Runner(){
	// 实现接口的所有方法
}

总结:接口可以看做一种特殊的抽象类,所有属性为公开静态常量,所有方法都为公开抽象方法。接口之间可以多继承,类可以实现多个接口。

如果抽象类和接口都可以使用的话,优先使用接口,因为避免单继承的局限。
在开发中,一个类不要去继承一个已经实现好的类,要么继承抽象类,要么实现接口。

工厂方法(FactoryMethod)模式
代理模式(Proxy)


注:以上笔记参考自尚硅谷

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值