JavaSE 笔记【三】面向对象

视频链接:Java零基础教程

封装性
对象的内存解析

堆(Heap):存放对象实例
栈(Stack):存储局部变量
方法区(Method Area):类信息、常量、静态变量、即时编译器编译后的代码

属性与局部变量

属性:可以用访问修饰符修饰,有默认初始化值,加载到堆空间中(static属性加载到方法区)
局部变量:没有访问修饰符,没有默认初始化值,加载到栈空间中

对象数组的内存解析

引用类型的变量,只可能存储两类值:null或地址值(含变量的类型)

可变个数的形参

JavaSE 5.0 中提供了Varargs(variable number of arguments)机制,允许直接定义能和多个实参相匹配的形参。从而,可以用一种更简单的方式,来传递个数可变的实参。可变形参可以理解为数组类型
具体使用:

  • 可变个数形参的格式:数据类型 … 变量名
  • 当调用可变个数参数的方法时,传入参数的个数可以是:0个、1个、2个…
  • 可变个数形参的方法与本类中方法名相同,形参不同的方法之间构成重载
  • 可变个数形参的方法与本类中方法名相同,形参类型也相同的数组之间不构成重载。二者不能共存
  • 可变个数形参在方法的形参中,必须声明在末尾
  • 可变个数形参在方法的形参中,最多只能申明一个可变形参
// 声明
public void show(String ... args) {
  for(int i = 0; i < args.length; i ++) {
    System.out.println(args[i]);
  }
}
// 调用
show("AA", "BB", "CC");
show(new String[]{"AA", "BB", "CC"});
四种访问修饰符

Java权限修饰符public、protected、provate置于类的成员定义前,用来限定对象对该类成员的访问权限

修饰符类内部同一个包不同包的子类同一个工程
privateyes---
(缺省)yesyes--
protectedyesyesyes-
publicyesyesyesyes

对于class的权限修饰只可以用publicdefault(缺省)

构造器

如果没有显式定义构造器,Java则提供默认无参构造器,构造器的访问权限与类的访问权限相同

JavaBean

JavaBean是一种Java语言写成的可重用组件,是指符合如下标准的Java类

  • 类是公共的
  • 有一个无参的公共的构造器
  • 有属性,且有对应的get、set方法

用户可以使用JavaBean将功能、处理、值、数据库访问和其他任何可以用Java代码创造的对象进行打包,并且其他的开发者可以通过内部的JSP页面、Servlet、其他JavaBean、applet程序或者应用来使用这些对象。用户可以认为JavaBean提供了一种随时随地的复制和粘贴的功能,而不用关心任何改变

this关键字

this理解为:当前对象或当前正在创建的对象

this调用构造器:使用 “this(形参列表)” 方式,调用本类中指定的其他构造器

“this(形参列表)” 必须声明在当前构造器的首行
构造器内部,最多只能声明一个 “this(形参列表)” ,用来调用其他的构造器

import关键字

如果使用的类或者接口是java.lang(language)包或同一个包下定义的,则可以省略import结构

如果在源文件中,使用了不同包下的同名的类,则必须至少有一个类需要以全类名的方式显示

使用 ”xxx.*“ 方式表明可以调用 xxx 包下的所有结构。但是如果使用的是 xxx 子包下的结构,则任需要显式导入

import static:导入指定类或接口中的静态结构:属性或方法

import static java.lang.System.*;
继承性

一旦子类A继承父类B以后,子类A中就获取了父类B中声明的所有的属性和方法。特别的,父类中声明为private的属性和方法,子类继承父类以后,任然认为获取了父类中私有的结构,只是因为封装性的影响,使得子类不能直接调用父类的结构而已

Object

如果我们没有显式的声明一个类的父类,则此类继承与 java.lang.Object 类
所有的java类(除 java.lang.Object 类之外)都直接或间接的继承于 java.lang.Object 类

Override

① 子类重写的方法的方法名和形参列表与父类被重写的方法名和形参列表相同
② 子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符
> 特殊情况:子类不能重写父类中声明为private权限的方法
返回值类型
> 父类被重写的返回值类型是void,则子类重写的方法的返回值类型只能是void
> 父类被重写的返回值类型是A类型,则子类重写的方法的返回值类型可以是A类或A类的子类
> 父类被重写的返回值类型是基本数据类型(比如:double),则子类重写的方法的返回值类型必须是相同的数据类型(比如:double)
④ 子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型

子类和父类中的同名同参数的方法要么都声明为非static的(考虑重写),要么都声明为static的(不是重写)

属性不存在覆盖

super

“super(形参类表)” 的使用,必须声明在子类构造器的首行
在类的构造器中,针对于”this(形参列表)“或”super(形参列表)“只能二选一,不能同时出现
在构造器的首行,没有显示的声明”this(形参列表)“或”super(形参列表)“,则默认调用的是父类中无参的构造器 super();
在类的多个构造器中,至少有一个类的构造器中使用了”super(形参列表)“,调用父类的构造器

明确:虽然创建子类对象时,调用了父类的构造器,但是自始至终就创建过一个对象,即为new的子类对象

多态性

多态性:一个事务的多种形态,父类的引用指向子类的对象

Person p = new Man(); // 方法:编译看左边,运行看右边

多态的使用:当调用子父类同名同参数的方法时,实际执行的是子类重写父类的方法 — 虚拟方法调用
我们在编译期,只能调用父类中声明的方法,但在运行期,实际执行的是子类重写父类的方法
多态性的使用前提:① 类的继承关系 ② 方法的重写

对象的多态性,只适用于方法,不适用于属性

p.age // 属性:编译、运行都看左边
// Person、Man中都存在age,调用的是Person类中的age
多态性使用举例
package xxx.xxx.xxx;

import java.sql.Connection;

public class AnimalTest {

  public static void main(String[] args) {

    AnimalTest test = new AnimalTest();
    test.func(new Dog());
    test.func(new Cat());
  }

  public void func(Animal animal) { // Animal animal = new Dog();
    animal.eat();
    animal.shout();
  }

//  public void func(Dog dog) {
//    dog.eat();
//    dog.shout();
//  }
//  public void func(Cat cat) {
//    cat.eat();
//    cat.shout();
//  }

}

class Animal {

  public void eat() {
    System.out.println("动物: 进食");
  }

  public void shout() {
    System.out.println("动物: 叫");
  }
}

class Dog extends Animal {

  public void eat() {
    System.out.println("狗吃骨头");
  }

  public void shout() {
    System.out.println("汪!汪!汪!");
  }
}

class Cat extends Animal {

  public void eat() {
    System.out.println("猫吃鱼");
  }

  public void shout() {
    System.out.println("喵!喵!喵!");
  }
}

// 举例二
class Order {
  public void method(Object obj) {
  }
}

// 举例三
class Driver {
  public void daData(Connection conn) { // conn = new MySQLConnection(); / conn = new OracleConnection();
    // 规范的步骤去操作数据
//    conn.method1();
//    conn.method2();
//    conn.method3();
  }
}
虚拟方法调用

子类中定义了与父类同名同参数的方法,在多态情况下,将此时父类的方法称为虚拟方法,父类根据付给它的不同子类对象,动态调用属于子类的该方法。这样的方法调用在编译期是无法确定的

Person e = new Stendent();
e.getInfo(); // 调用Student类的getInfo()方法

编译时类型运行时类型

编译时e为Person类型,而方法的调用是在运行时确定的,所以调用的是Student类的getInfo() 方法— 动态绑定

多态是运行时行为,证明:简单工厂模式

重载重写的区别

重载,是指允许存在多个同名方法,而这些方法的参数不同。编译器根据方法不同的参数表,对同名方法的名称做修饰。对于编译器而言,这些同名方法就成了不同的方法。它们的调用地址在编译期就绑定了。Java的重载是可以包括父类和子类的,即子类可以重载父类的同名不同参数的方法。
所以:对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法,这成为“早绑定”或“静态绑定”;
而对于多态,只有等到方法调用的那一刻,编译器才会确定所要调用的具体方法,这称为“晚绑定”或”动态绑定

Bruce Eckel: "不要犯傻,如果它不是晚绑定,它就不是多态“

向下转型

有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型,导致编译时,只能调用父类中声明的属性和方法。子类持有的属性和方法不能调用

Man m = (Man)p; // 如何才能调用子类特有的属性和方法? 向下转型,使用强制类型转化 输出类信息包括:类型@地址值,使用强转时,可能出现ClassCastException的异常

多态性

instanceof

a instanceof A:判断对象a是否是类A的实例,如果是,返回true,如果不是,返回false

使用情景:为了避免向下转型时出现ClassCastException的异常(向下转型必须转化为子类对象),我们在向下转型之前,先进行instanceof的判断,一旦返回true,就进行向下转型。如果返回false,不进行向下转型

如果a instanceof A返回true,则a instanceof B也返回true(其中,类B是类A的父类)

内置对象及关键字
Object类的使用
  1. 属性:无
  2. 方法:
    • equals()
    • toString()
    • getClass()
    • hashCode()
    • clone()
    • finalize():在对象回收之前,会调用对象的 finalize() 方法
    • wait()
    • notify()
    • nofifyAll()
== 和 equals() 区别
==equals
运算符方法
可以使用在基本数据类型变量和引用数据类型变量中只能适用于引用数据类型
如果比较的是基本数据类型变量:比较两个变量保存的数据是否相等(不一定需要同一个类型,比较的是值,不关心类型),如果比较的是引用数据类型变量:必须保证符号左右两边的变量类型一致。比较两个对象的地址值是否相同,即两个引用是否指向同一个对象实体Objcet类中equals() 的定义:比较两个对象的地址值是否相同,即两个引用是否指向同一个对象实体 String、Date、FIle、包装类等都重写了Object类中equals()方法。重写以后,比较的不是两个引用的地址是否相同,而是比较两个对象的“实体内容”是否相同

通常情况下,我们自定义的类如果使用equals()的话,也通常是比较两个对象的“实体内容”是否相同。那么,我们就需要对Object类中的equals()进行重写

重写equals()方法的原则

  • 对称性
  • 自反性
  • 传递性
  • 一致性
  • x.equals(null) 返回false
  • x.equals(和x不同类型的对象) 返回false
toString()

Object类中toString()定义

public String toString() {
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

当我们输出一个对象的引用时,实际上就是调用当前对象的toString()

单元测试

Java中的JUnit单元测试

  1. 导入 JUnit 4
  2. 创建Java类,进行单元测试
    此时的Java类要求:① 此类是public的 ② 此类提供公共的无参的构造器
  3. 此类中声明单元测试方法
    此时的单元测试方法:方法的权限是public,没有返回值,没有形参
  4. 此单元测试方法上需要声明注解:@Test,并在单元测试类中导入:import org.junit.Test;
  5. 声明好单元测试方法以后,就可以在方法体内测试相关的代码
包装类

针对八种基本数据类型定义相应的引用类型–包装类(封装类)
有了类的特点,就可以调用类中的方法,Java才是真正的面向对象

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

Byte、Short、Integer、Long、Float、Double 父类:Number

基本类型、包装类与String类的转换

包装类转化

包装类常见面试题
@Test
public void test1() {
  Object o1 = true ? new Integer(1) : new Double(2.0);
  System.out.println(o1); // 1.0 三元运算符自动类型提升
}

@Test
public void test2() {
  Object o2;
  if (true) {
    o2 = new Integer(1);
  } else {
    o2 = new Double(2.0);
  }
  System.out.println(o2); // 1
}
@Test
public void test3() {
  Integer i = new Integer(1);
  Integer j = new Integer(1);
  System.out.println(i == j); // false

  Integer m = 1;
  Integer n = 1;
  System.out.println(m == n); // true

  Integer x = 128;
  Integer y = 128;
  System.out.println(x == y); // false, IntegerCache 缓存了-128~127,类似常量池。如果使用自动装箱的方式,给Integer赋值的范围在-128~127范围内时,可以直接使用数组中的元素,不用再去new了,目的,提高效率
}
static

静态的、某些特定的数据在内存空间里只有一份
修饰:属性、方法、代码块、内部类

静态变量

① 静态变量随着类的加载而加载。可以通过“类.静态变量”的方式进行调用
② 静态变量的加载要早于对象的创建
③ 由于类只会加载一次,则静态变量在内存中也只会存在一份:存在方法区的静态域中

-类变量实例变量
×
对象

静态方法

① 随着类的加载而加载。可以通过“类.静态方法”的方式进行调用
② 静态方法中,只能调用静态的方法或属性
非静态方法中,既可以调用非静态的方法或属性,也可以调用静态的方法或属性
③ 在静态的方法内,不能使用this关键字、super关键字

-静态方法实例方法
×
对象
单例模式
  • 私有化构造器
  • 创建私有静态实例变量
  • 提供公共静态的方法,返回静态实例变量

饿汉式:对象加载时间过长,线程安全
饱汉式:延迟对象的创建,线程不安全,需要加线程安全限制

应用场景

  • 网站的计数器
  • 应用程序的日志应用
  • 数据库连接池
  • 读取配置文件的类
  • Application也是单例的典型应用
  • Windows的Task Manager(任务管理器)
  • Windows的Recycle Bin(回收站)
代码块

代码块作用:用来初始化类、对象
代码块如果有修饰的话,只能使用static

静态代码块非静态代码块
随着类的加载而执行创建对象时执行
只执行一次每次创建对象执行
初始化类的信息对对象的属性进行初始化
如果一个类中定义了多个静态代码块,则按照声明的先后顺序执行如果一个类中定义了多个非静态代码块,则按照声明的先后顺序执行
静态代码块优先于非静态代码块执行-
只能调用静态的属性、静态的方法、不能调用非静态的结构可以调用静态的属性、静态的方法,或非静态的属性、非静态的方法
属性赋值的先后顺序

① 默认初始化
② 显示初始化 / 代码块中赋值 (取决于代码的先后顺序)
③ 构造器初始化
④ 创建对象,通过“对象.属性”或“对象.方法”的方式,进行赋值

final关键字

final可以用来修饰的结构:类、方法、变量
final修饰类:此类不能被其他类所继承
final修饰方法:此方法不可以被重写
final修饰变量:此时的”变量“称为一个常量
> 修饰成员变量,可以赋值的位置:显示初始化,代码块中赋值、构造器中初始化(所有构造器都必须初始化变量)
> 修饰局部变量

抽象类与接口
抽象类与抽象方法

有时将一个父类设计得非常抽象,以至于它没有具体的实例,这样的类叫做抽象类

abstract 可以用来修饰的结构:类、方法
> 抽象类:不能实例化。抽象类中一定有构造器,便于子类实例化时使用。开发中都会提供抽象类的子类,完成相关的操作
> 抽象方法:只有方法的声明,没有方法体。包含抽象方法的类一定是一个抽象类。反之,抽象类中可以没有抽象方法。若子类重写了父类中的所有抽象方法后,此子类方可实例化,若子类没有重写父类中的所有抽象方法,则此子类也是一个抽象类,需要使用abstract修饰

abstract使用上的注意点:

  1. abstract不能用来修饰:属性、构造器等结构
  2. abstract不能用来修饰私有方法、静态方法、final的方法、final的类

抽象类体现了模板方法的设计模式:当功能内部一部分实现是确定的,一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去实现

创建抽象类的匿名子类对象
Person p = new Person() { // Person为接口或抽象类
	@Override
	public void eat() {
		System.out.println("吃东西")}
}
接口

定义接口中的成员

JDK1.7及以前:只能定义全局常量和抽象方法
> 全局常量:public static final,书写时可以省略
> 抽象方法:public abstract,书写时可以省略

JDK8:除了全局变量和抽象方法外,还可以定义静态方法(调用:接口名.方法名,实现类不能调用)、默认方法(通过实现类的对象调用)

接口中不能定义构造器!意味着接口不可以实例化。接口和接口之间可以继承,而且可以多继承。如果子类(或实现类)继承的父类和实现的接口中声明了同名参数的方法,那么子类在没有重写此方法的情况下,默认调用的是父类中同名参数的方法(类优先原则)。如果实现类实现了多个接口,而这多个接口中定义了同名参数的默认方法,那么在实现类没有重写此方法的情况下,报错(接口冲突),这就需要我们必须在实现类中重写此方法

如何在子类(实现类)的方法中调用父类、接口中被重写的方法? (父类/接口名).super.方法名

接口体现了代理工厂的设计模式
应用场景:

  • 安全代理
  • 远程代理
  • 延迟代理

分类

  • 静态代理
  • 动态代理

笔试题

interface A {
	int x = 0;
}
class B {
	int x = 1;
}

class C extends B implements A {
	public void pX() {
		// System.out.println(x); // 编译不通过,因为x是不明确的
		System.out.println(super(x)); // 1
		System.out.println(A.x); // 2
	}
	public static void main(String[] args) {
		new C().pX();
	}
}
内部类

局部内部类、成员内部类

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值