引言

面向对象是一种编程思想,是程序员思考问题的思维方式,以此为编程思想指导的程序设计,又称为面向对象程序设计( Object-Oriented Programing ,OOP ),面向对象设计设计思想,关注点为对象( 任何事物,万事万物 ),而非具体过程。
面向过程编程思想:也是一种编程思维,它关注的就是问题解决的步骤和实现过程,这种编程思维往往涉及到细节数据结构和算法本身。

对于不同编程思想,有不同的语言,C语言就是典型面向过程的设计语言,Java 就是面向对象的程序设计语言。

程序:

模拟现实世界 解决现实问题 而使用计算机编程语言编写的指令集合

1.面向对象:

  1. 世界由无数个对象组成;<br /> 一切客观存在的事物都是对象,任何对象都有自己的特征和行为;<br /> <br /> 特征:代表对象有什么<br /> 行为:代表对象能做什么

一个类创建多个对象:

  1. 类:定义了对象应有的特征和行为 ,类是对象的模板 <br /> 对象:拥有多个特征和行为的实体,对象是类的实例<br /> <br /> <br /> ** 局部变量 ** ** 实例变量**<br /> <br /> 方法或方法内的结构中。 类的内部,方法的外部<br /> 无默认值 与数组相同<br /> 从定义行到包含其结构结束 本类有效<br /> 不允许与局部变量重名 不允许与实例变量重名,可与局部变量重名,局部变量优先

对象内存分布

对象是自定义类的实例,自定义类是一种引用数据类型,所以这里和前面章节学的数组一样,对象也是一种引用数据类型,它的存储必然也分堆栈,对象本质在堆内申请空间,对象引用被存在栈里面。
图:
5.对象 - 图1

类与对象的关系

类和对象的关系就是,抽象与具体的关系,图纸与实物的关系,一个类可以实例化出多个对象,每个对象拥有独立的内存空间,尤其是实例属性,各自有一份。
图:
5.对象 - 图2
一个类可以 实例化出 N 个 多个对象

2.类的定义:


属性:变量表示 ,(实例变量)类的内部 方法的外部;
方法:实例方法 不再书写static

方法重载:一个类定义多个相同名称的方法

  1. 要求:<br /> 方法名相同,参数列表不同(类型 个数 顺序 ), 与访问修饰符 返回值类型 无关,<br /> 好处:灵活 方便 屏蔽使用差异;<br /> 同名不同参<br />

构造方法:

  1. 系统默认提供无参构造方法;<br /> 当你定义一个有参构造时,必须定义一个无参构造;<br /> 方法名与类名相同;<br /> 无参构造方法;<br /> 使用有参构造方法 为属性赋值(初始化数据成员);

this关键字:

this.name = name; 表示把局部变量赋值给实例变量
this(xxx)访问本类中的其他方法;


本类 同包 非同包子类 其他

private 1 0 0 0

default 1 1 0 0

protected 1 1 1 0

public 1 1 1 1

3.面向对象的三大特点

封装:

  1. private:意为私有的意思,指的是如果一个属性 或者 方法被其修饰,那么这个属性和方法就只能在其声明的类中使<br /> 定义一个类,该类有一个成员变量,通过构造方法将其进行赋初值,<br /> 并提供该成员的getXXX()和setXXX(参数)方法<br /> 修改访问修饰符,并提供公共的get/set方法
  1. class Foo{
  2. private int age;
  3. public void aa(){
  4. }
  5. }
  6. class TestFoo{
  7. public static void main(String[] args){
  8. Foo foo = new Foo();
  9. foo.age=100; //报错,此时age是无法被访问的。
  10. }
  11. }

继承:

  1. **extends:**<br /> 产生继承关系之后,子类可以使用父类的属性和方法,子类也可以定义自己的方法和属性
  2. **好处**:提高代码的复用性,提高代码的可拓展性;<br /> 单继承,一个类只能有一个直接父类,但可以多级继承<br /> 构建子类对象先构建父类对象<br /> <br /> <br /> **不可继承**:<br />**类中的构造方法,只负责创建本类对象,不可继承 可用super去调用**<br /> private修饰的属性和方法<br /> 父子类不在同一个包时,default修饰的属性和方法,(仅同包可见)<br /> <br /> 方法的重写(覆盖):<br />参数列表与被重写方法的参数列表必须完全相同。<br />返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类(java5 及更早版本返回类型要一样,java7 及更高版本可以不同)。<br /> 访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected。<br />父类的成员方法只能被它的子类重写。<br />声明为 final 的方法不能被重写。<br />声明为 static 的方法不能被重写,但是能够被再次声明。<br />子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private final 的方法。<br />子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public protected 的非 final 方法。<br />或者比被重写方法声明的更广泛的强制性异常,反之则可以。<br />**构造方法不能被重写**。<br />如果不能继承一个类,则不能重写该类的方法。<br />** **<br />** 1.发生方法重写的两个方法返回值、方法名、参数列表必须完全一致(子类重写父类的方法)。**<br />**2.子类抛出的异常下不能超过父类相应方法抛出的异常(子类异常不能大于父类异常)。**<br />**3.子类方法的访问级别不能低于父类相应方法的访问级别(子类访问级别不能低于父类访问级别)。**<br /> <br /> 区别点 重载方法 重写方法<br /> 参数列表 必须修改 一定不能修改<br /> 返回类型 可以修改 一定不能修改<br /> 异常 可以修改 可以减少或删除,一定不能抛出新的或者更广的异常<br /> 访问 可以修改 一定不能做更严格的限制(可以降低限制<br /> @Override 它是一个注解,用来检验方法是否重写正确。它不是必要的,建议加上防止重写错误。
  1. public class Animal {
  2. protected String name;
  3. protected int age;
  4. protected String sex;
  5. public Animal(){
  6. }
  7. public Animal(String name, int age, String sex) {
  8. this.name = name;
  9. this.age = age;
  10. this.sex = sex;
  11. }
  12. public void eat(){
  13. System.out.println("吃");
  14. }
  15. public void sleep(){
  16. System.out.println("睡");
  17. }
  18. }
  19. public class Dog extends Animal{
  20. //共性属性和方法无需定义了,继承就可以了
  21. //子类特有的属性和行为
  22. String color;
  23. public void lookDoor(){
  24. System.out.println("看门");
  25. }
  26. //重新继承的方法
  27. @Override
  28. public void eat() {
  29. System.out.println("吃骨头");
  30. }
  31. @Override
  32. public void sleep() {
  33. System.out.println("躺着睡");
  34. }
  35. }

super 关键字
如果子类 存在方法重写 和 属性遮蔽 , 可以通过 super.xxx 来强制区分。

  1. /**
  2. * 理解Super的作用
  3. */
  4. public class SuperDemo {
  5. public static void main(String[] args) {
  6. Son son = new Son();
  7. son.happy();
  8. }
  9. }
  10. class Father{
  11. String name="张飞";
  12. int age;
  13. public void happy(){
  14. System.out.println("打麻将.....");
  15. }
  16. }
  17. class Son extends Father{
  18. String hobby;
  19. String name="刘备";
  20. @Override
  21. public void happy() {
  22. super.happy();
  23. System.out.println("蹦迪......"+super.name);
  24. }
  25. }

多态:


多态是同一个行为具有多个不同表现形式或形态的能力。

优点: 1. 消除类型之间的耦合关系
2. 可替换性
3. 可扩充性
4. 接口性
5. 灵活性
6. 简化性


多态存在的三个必要条件

继承(直接或者间接)
重写
父类引用指向子类对象:Parent p = new Child();
父类引用尽可以调用父类声明的属性或者方法,不可调用子类独有的属性或者方法


子类重写了父类的方法,以父类类型引用调用此方法时,优先执行子类中重写的方法(遵循重写原则)


父类作为方法形参 实现多态

  1. //主人
  2. public class Master {
  3. /* 这两个方法重载,可以使用多态优化为一个方法
  4. //接收一个 Dog 类型的参数
  5. public void feed( Dog obj ){
  6. obj.eat();
  7. }
  8. //接收一个 Cat 类型的参数
  9. public void feed( Cat obj ){
  10. obj.eat();
  11. }
  12. */
  13. //接收一个 Animal 类型的参数
  14. public void feed( Animal obj ){ //Animal obj = cat = new Cat();
  15. obj.eat();
  16. }
  17. public static void main(String[] args) {
  18. Dog dog = new Dog();
  19. Cat cat = new Cat();
  20. Master master = new Master();
  21. master.feed( cat ); // 这里可以传 cat 和 dog对象 均可。
  22. }
  23. }

父类作为返回值返回 实现多态

  1. //主人
  2. public class Master {
  3. //接收一个 Animal 类型的参数
  4. public void feed( Animal obj ){
  5. obj.eat();
  6. }
  7. //返回值为父类类型
  8. public Animal kill(){
  9. Random random = new Random();
  10. int num = random.nextInt(2);
  11. if( num == 0 ){ //杀个狗
  12. Dog dog = new Dog();
  13. return dog;
  14. }else{ //杀个猫
  15. Cat cat = new Cat();
  16. return cat;
  17. }
  18. }
  19. }
  1. **3.父类型定义数组,可以保存各种子类对象**
  1. public static void main(String[] args) {
  2. Animal[] arr = new Animal[4];
  3. arr[0] = new Cat();
  4. arr[1] = new Dog();
  5. arr[2] = new Cat();
  6. arr[3] = new Dog();
  7. for( Animal an : arr){
  8. an.eat();
  9. }
  10. }

多态好处:屏蔽子类间的差异,灵活 耦合度低

  1. public static void main(String[] args) {
  2. show(new Cat()); // 以 Cat 对象调用 show 方法
  3. show(new Dog()); // 以 Dog 对象调用 show 方法
  4. Animal a = new Cat(); // 向上转型
  5. a.eat(); // 调用的是 Cat 的 eat
  6. Cat c = (Cat)a; // 向下转型
  7. c.work(); // 调用的是 Cat 的 work
  8. }
  9. public static void show(Animal a) {
  10. a.eat();
  11. // 类型判断
  12. if (a instanceof Cat) { // 猫做的事情
  13. Cat c = (Cat)a;
  14. c.work();
  15. } else if (a instanceof Dog) { // 狗做的事情
  16. Dog c = (Dog)a;
  17. c.work();
  18. }
  19. }

instanceof 关键字

作用是判断一个引用是否为指定的类型 语法为 引用 instanceof 类型 如果该表达式成立返回 true ,否则返回false

  1. public static void main(String[] args) {
  2. System.out.println("--------转型-----------");
  3. // 自动类型提升 : 安全 自动完成
  4. Animal an = new Dog();
  5. // 向下转型 : 不安全 手动完成
  6. //Dog dd = (Dog)an; //java.lang.ClassCastException: case6.Cat cannot be cast to case6.Dog
  7. //dd.lookDoor();
  8. // 转型前判断一下
  9. if( an instanceof Cat ){
  10. Cat xx= (Cat)an;
  11. xx.catchMouse();
  12. }
  13. if( an instanceof Dog ){
  14. Dog yy = (Dog)an;
  15. yy.lookDoor();
  16. }
  17. }

4.三个关键字

abstract:抽象

  1. 抽象类:不完整不具体的类,所以不能被实例化;
  1. package case4;
  2. /**
  3. * 动物: 抽象类
  4. */
  5. public abstract class Animal { // 这里使用 abstract 关键字修饰
  6. protected String name;
  7. public Animal() {
  8. }
  9. public Animal(String name) {
  10. this.name = name;
  11. }
  12. public void sleep(){
  13. // 这里的方法比较抽象,无法给出具体实现,这里不编写任何代码,称为空实现
  14. }
  15. public void eat(){
  16. // 这里的方法比较抽象,无法给出具体实现,这里不编写任何代码,称为空实现
  17. }
  18. }
  1. 抽象方法:没有方法体,大括号都不用给,不能被实现,子类必须重写,否则子类还是抽象类;
  1. package case4;
  2. /**
  3. * 动物: 抽象类
  4. */
  5. public abstract class Animal { // 这里使用 abstract 关键字修饰
  6. protected String name;
  7. public Animal() {
  8. }
  9. public Animal(String name) {
  10. this.name = name;
  11. }
  12. public abstract void sleep(); // 这里使用 abstract 修饰方法,设计为抽象方法,不给出具体实现,代码更简洁了
  13. public abstract void eat(); // 这里使用 abstract 修饰方法,设计为抽象方法,不给出具体实现,代码更简洁了
  14. }
  1. 抽象类中不一定有抽象方法,有抽象方法类的必须是抽象类<br /> 不可以new 自己的类<br />**抽象类的作用**:<br />抽象类和普通类一样,可以定义普通类全部的内容。少了创建本身实例的能力,多了可以定义抽象方法的作用。可以作为父类使用,可以更好更简洁的使用多态的灵活性,代码更精简。<br />

static:静态的

  1. 实例属性:每个对象各自持有的独立空间(多份),对象单方面修改,不会影响其他对象;<br /> 静态属性:是整个类共同持有的共享空间,任何对象修改,都会影响其他对象;<br /> 静态可以修饰属性和方法;成为静态属性(类属性),静态方法(类方法)<br /> 静态成员是全类所有对象共有享的成员<br /> 可以直接类名访问。<br />
  1. /**
  2. * 学生
  3. */
  4. public class Student {
  5. String name; //名字:实例属性
  6. static String nation; //国籍: 静态属性
  7. //实例方法 : 可以访问静态和实例属性
  8. public void show(){
  9. System.out.println("静态属性nation"+nation+"实例属性"+name);
  10. }
  11. }
  12. package case6;
  13. public class TestStudent {
  14. public static void main(String[] args) {
  15. //同意赋值
  16. Student.nation="新加坡";
  17. Student s1 = new Student();
  18. s1.name="张三";
  19. s1.show();
  20. System.out.println("------------------");
  21. Student s2 = new Student();
  22. s2.name = "李四";
  23. s2.show();
  24. }
  25. }


静态方法允许直接访问静态成员,不能直接访问非静态成员
静态方法中不允许使用this/super 关键字
静态方法可以继承,不能重写,没有多态;

  1. package case6;
  2. /**
  3. * 学生
  4. */
  5. public class Student {
  6. String name; //名字:实例属性
  7. static String nation; //国籍: 静态属性
  8. //实例方法 : 可以访问静态和实例属性?
  9. public void show(){
  10. System.out.println("静态属性nation"+nation+"实例属性"+name);
  11. }
  12. //定义一个静态方法 : 静态方法不能访问实例相关的数据(this 实例属性)
  13. public static void study(){
  14. //System.out.println("我是"+this.name + "我来之"+nation); 报错
  15. System.out.println("我来之"+nation);
  16. }
  17. }
  18. public class TestStudent {
  19. public static void main(String[] args) {
  20. //访问静态属性
  21. Student.nation="新加坡";
  22. //访问静态方法
  23. Student.study();
  24. }
  25. }


普通代码块
普通代码块,是实例级别的信息,随着对象创建而执行,且每创建一个对象都会执行,执行多次,用于初始化实例属性,当然这是可选的,因为为实例属性赋值的方式可以有其他方式,比如构造器赋值,set方法属性赋值等。
动态代码块:创建对象时,出发动态代码块执行,
执行地位:初始化属性之后,构造方法代码之前
作用: 可为实例属性赋值,或必要的初始行为;

  1. public class Foo {
  2. String name = "SB"; //实例属性 setp 1
  3. { //普通代码块(动态代码块) setp 2
  4. System.out.println("代码块"+Foo.name);
  5. name = "NB";
  6. }
  7. public Foo() { //构造器 setp 3
  8. System.out.println("构造器....."+name);
  9. }
  10. public static void main(String[] args) {
  11. Foo f1 = new Foo();
  12. Foo f2 = new Foo();
  13. }
  14. }

可以总结 属性执行赋值 > 普通代码块执行> 构造器执行。


类加载:JVM首次使用某个类时,需要通过CLASSPATH查找该类的.class 文件
加载时机:创建对象 创建子类对象 访问静态属性 调用静态方法 主动加载 ClASS.forName(“全限定名”);


静态代码块:类加载时执行(执行一次)
执行地位:在静态属性初始化之后
作用: 可为静态属性赋值,或必要的初始行为;

  1. public class Foo {
  2. static String name = "SB"; //1 // 类加载(1加载 2验证 3准备 4初始初始化)
  3. static {
  4. System.out.println("代码块"+Foo.name);
  5. name = "NB"; //2
  6. }
  7. public Foo() { //3
  8. System.out.println("构造器....."+name);
  9. }
  10. public static void main(String[] args) {
  11. Foo f1 = new Foo();
  12. System.out.println(Foo.name);
  13. }
  14. }
  1. 加载: Foo.class 装载到内存(读文件)
  2. 验证: 验证是否是一个合法字节码,以及相关的语法校验。
  3. 准备: 开辟所需要的空间。
  4. 初始化: 从上到下异常执行赋值语句。

    面试题对象初始化过程

    目前,咱们初始化属性可能涉及的方式就很丰富了,直接赋值 ,动态代码块赋值 ,静态代码块赋值,构造器赋值,那么如果类之间存在继承关系时,属性初始化执行先后顺序是如何的呢? ```java package case8;

/**

  • 面试题: 对象创建 各初始化模块执行的先后顺序 */ public class Demo {

    public static void main(String[] args) {

    1. Son son = new Son();

    }

}

// 爷爷类 class YeYe{ { System.out.println(“爷爷动态代码块”); } static { System.out.println(“爷爷静态代码块”); }

  1. public YeYe() {
  2. System.out.println("爷爷构造器");
  3. }

} // 父类 class Father extends YeYe{

  1. {
  2. System.out.println("父类动态代码块");
  3. }
  4. static {
  5. System.out.println("父类静态代码块");
  6. }
  7. public Father() {
  8. System.out.println("父类构造器");
  9. }

}

// 子类 class Son extends Father{

  1. {
  2. System.out.println("Son动态代码块");
  3. }
  4. static {
  5. System.out.println("Son静态代码块");
  6. }
  7. public Son() {
  8. System.out.println("Son构造器");
  9. }

} ``` 输出: 递归向上执行静态的 静态代码块 -> 递归返回执行各级 动态代码块 和 构造器。
爷爷静态代码块
父类静态代码块
Son静态代码块
爷爷动态代码块
爷爷构造器
父类动态代码块
父类构造器
Son动态代码块
Son构造器

JDK 的类库中,就有充分使用static 案例, Math 数据工具类,提供了很多与数学运算相关的属性和方法( 都是静态的 )。

final:最后的,不可更改的

  1. 可修饰 方法 变量<br /> <br /> final 类:String Math System 此类不能被继承<br /> <br /> final 方法:不支持子类以覆盖的方式修改 可以重载<br /> <br /> final 变量:此变量值只能被赋值一次,不能被改变<br /> <br /> 局部常量:显式初始化之后<br /> <br /> 实例常量 final String name; 不在提供默认值 必须手动赋予初始值;<br /> 赋值时机 :显式初始化,动态代码块,构造方法;<br /> <br /> 静态常量 static final String name;不在提供默认值 必须手动赋予初始值;<br /> 赋值时机 :显示初始化,静态代码块<br /> <br /> 对象常量:final修饰基本类型常量 值不可变<br /> final修饰引用类型常量 地址不可变