【Java笔记】09 面向对象

一、类与对象

【Java笔记】09 面向对象 - 图1

  • 属性
    成员属性=属性=field 访问修饰符 属性类型 属性名;
    访问修饰符:public protected 默认 private
    属性不赋值有默认值,跟数组默认值相同
  • 方法(成员方法)
    当程序执行到方法时,开辟一个独立的栈空间;
    执行完毕,或者到return语句时,返回;
    返回到调用方法的语句,继续执行后面的代码
    方法的定义:
    访问修饰符 返回数据类型 方法名(形参列表..){//方法体
    语句;
    return 返回值;
    }

同一个类中的方法调用,直接调用即可,不需要创建对象

  1. class A{
  2. //同一个类中的方法调用,直接调用即可,不需要创建对象
  3. pubilc void print(int n){
  4. System.out.println("输出");
  5. }
  6. public void test(){
  7. print();
  8. }
  9. }

跨类中的方法调用:A类调用B类方法,需要通过对象名调用。即创建B类的对象,再调用方法

二、方法重载

java中允许同一个类中,多个同名方法的存在,但要求形参列表不一致
方法名:必须相同
形参列表:必须不同(形参类型或个数或顺序,至少有一样不同,参数名无要求)
返回类型:无要求

三、可变参数

java允许将同一个类中多个同名同功能但参数个数不同的方法,封装成一个方法
可变参数的实参可以为0个或任意多个
可变参数的实参可以为数组
可变参数的本质就是数组
可变参数可以和普通类型的参数一起放在形参列表,但必须保证可变参数在最后
一个形参列表中最多只能出现一个可变参数

  1. public class VarParameter{
  2. public static void main(String[] args){
  3. //求2个、3个、4个...参数的和
  4. HspMethod m = new HspMethod();
  5. System.out.println(m.sum(1,5,100));
  6. System.out.println(m.sum(1,19));
  7. }
  8. }
  9. class HspMethod{
  10. //int...表示接受的是可变参数,类型是int,即可接收多个int(0-多)
  11. //使用可变参数时,可以当做数组来使用,即nums可以当做数组
  12. //遍历nums求和即可
  13. public int sum(int... nums){
  14. int res = 0;
  15. for(int i =0;i < nums.length;i++){
  16. res += nums[i];
  17. }
  18. return res;
  19. }
  20. }

四、作用域

1.java中,主要的变量就是属性(成员变量)和局部变量
2.局部变量一般是指在成员方法中定义的变量
3.Java作用域分类
全局变量:即属性,作用域为整个类
局部变量:除了属性以外的其他变量,作用域为定义它的代码块中
4.属性可以不赋值,直接使用,因为有默认值,局部变量必须赋值后才能使用,没有默认值
5.作用域范围
全局变量/属性:可以被本类使用,或其他类使用(通过对象调用)
局部变量:只能在本类中对于的方法中使用
6.全局变量可以加修饰符,局部变量不能加修饰符

五、构造方法/构造器

构造方法又叫构造器,是类的一种特殊的方法,主要作用是完成对新对象的初始化
方法名和类名相同
没有返回值
创建对象时,系统自动的调用该类的构造器完成对象的初始化
[修饰符] 方法名(形参列表){
方法体;
}
1.构造器的修饰符可以默认,也可以时public protected private
2.构造器没有返回值
3.方法名和类名字必须一样
4.参数列表和成员方法规则一样
5.一个类可以定义多个不同的构造器,即构造器重载
6.定义了自己的构造器,默认构造器就覆盖了,除非显式定义一下

  1. public class Constructor{
  2. public static void main(String[] args){
  3. //new一个对象时,直接通过构造器初始化
  4. Person p1 = new Person("jack",3);
  5. }
  6. }
  7. class Person{
  8. String name;
  9. int age;
  10. public Person(String pName,int pAge){
  11. name = pName;
  12. age = pAge;
  13. }
  14. }

六、this

java虚拟机给每个对象分配this,代表当前对象
this 关键字可以用来访问本类的属性、方法、构造器
this 用于区分当前类的属性和局部变量
访问成员方法的语法:this.方法名(参数列表);

  1. class T{
  2. public void f1(){
  3. System.out.println("f1()");
  4. }
  5. public void f2(){
  6. System.out.println("f2()");
  7. //调用本类的f1
  8. //第一种方法
  9. f1();
  10. //第二种方法
  11. this.f1();
  12. }
  13. }

访问构造器语法:this(参数列表); 注意只能在构造器中使用(即只能在构造器中访问另外一个构造器, 必须放在第一 条语句)

  1. class T{
  2. public T(){
  3. this("jack",20);//访问构造器语法:this(参数列表);必须放置第一条语句
  4. System.out.println("T()构造器");
  5. }
  6. public T(String name,int age){
  7. System.out.println("T(String name,int age)构造器");
  8. }
  9. }

this 不能在类定义的外部使用,只能在类定义的方法中使用

七、访问修饰符

公开 public
受保护 protected 对子类和同一包的类公开
默认 对同一个包的类公开
私有 private 不对外公开
只有默认和public可以修饰类

八、封装

把抽象出的数据[属性]和对数据的操作[方法]封装在一起,数据被保护在内部,程序的其他部分只有通过被授权的操作[方法],才能对数据进行操作。
步骤:
1.先对属性私有化,让外部不能直接修改属性
2.提供一个公共的set方法,用于对属性判断并赋值

  1. public void setXxx(类型 参数名){
  2. //验证
  3. 属性 = 参数名;
  4. }

3.提供一个公共的get方法,用于获取属性的值

  1. public getXxx(){//权限判断
  2. return xx;
  3. }
  • 有构造器时想使用set方法,在构造器中使用set
  1. public Person(String name, int age, double salary) {
  2. // this.name = name;
  3. // this.age = age;
  4. // this.salary = salary;
  5. setSalary(salary);
  6. setAge(age);
  7. setName(name);
  8. }

九、继承

多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类(基类,超类),在父类中定义这些相同的属性和方法,子类(派生类)通过extends声明继承父类
子类继承了所有的属性和方法,但私有属性和方法不能在子类直接访问,要通过父类公共的方法;
创建子类时,不管使用子类的哪个构造器,默认都会调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用super指定使用父类的哪个构造器,即在子类构造器中写super(对应参数列表);
super()使用时放在构造器的第一行,super只能在构造器中使用;
super()和this()都只能放在第一行->不能同时存在;
java所有类都是Object类的子类,Object是所有类的基类;
父类构造器的调用不限于父类,而是一直往上追溯;
java单继承,子类最多只能直接继承一个父类;
子类和父类之间要满足is-a关系;

  1. public class Encap {
  2. public static void main(String[] args) {
  3. Son son = new Son();
  4. //内存的布局
  5. // 要按照查找关系来返回信息
  6. // (1) 首先看子类是否有该属性
  7. // (2) 如果子类有这个属性,并且可以访问,则返回信息
  8. // (3) 如果子类没有这个属性,就看父类有没有这个属性(如果父类有该属性,并且可以访问,就返回信息..)
  9. // (4) 如果父类没有就按照(3)的规则,继续找上级父类,直到 Object...
  10. System.out.println(son.name);//返回就是大头儿子
  11. //System.out.println(son.age);//会报错,age是私有的属性
  12. System.out.println(son.getAge());//返回的就是 39
  13. System.out.println(son.hobby);//返回的就是旅游
  14. }
  15. }
  16. class GrandPa {//爷类
  17. String name = "大头爷爷";
  18. String hobby = "旅游";
  19. }
  20. class Father extends GrandPa {//父类
  21. String name = "大头爸爸";
  22. private int age = 39;
  23. public int getAge() {
  24. return age;
  25. }
  26. }
  27. class Son extends Father { //子类
  28. String name = "大头儿子";
  29. }

十、super关键字

访问父类的属性、方法、构造器:
1.访问属性(不能访问父类的private属性)
super.属性名;
2.访问父类的方法,不能访问父类的private方法
super.方法名(参数列表);
3.访问父类的构造器
super(参数列表); 只能放在第一句
子类、父类方法有重名时,为了访问父类的成员,用super。没有重名,super,this,直接访问一样
找方法时,顺序:
1.先找本类,如果有,调用
2.如果没有,则找父类。父类如果有,并可以调用,则调用
3.如果父类没有,继续找父类的父类,知道Object
查找方法过程中,找到了但不能访问(比如private),报错

十一、方法重写/覆盖

子类有一个方法和父类的某个方法的名称、返回类型、参数一样,子类的这个方法覆盖了父类的方法
返回类型父类的返回类型是子类的父类也可以。比如父类返回类型是Object,子类是String
子类方法不能缩小父类方法的访问权限

  • 比较方法重载和重写 | 名称 | 发生范围 | 方法名 | 形参列表 | 返回类型 | 修饰符 | | —- | —- | —- | —- | —- | —- | | 重载 | 本类 | 必须一样 | 类型,个数或顺序至少有一个不同 | 无要求 | 无要求 | | 重写 | 父子类 | 必须一样 | 相同 | 子类重写的方法,返回类型和父类的返回类型一直或者是其子类 | 子类方法不能缩小父类方法的访问范围 |

多态

问题:代码复用性不高
解决方案:多态—>方法或对象有多种形态

  1. 方法的多态
    方法重载、重写体现多态
  2. 对象的多态
    (1)一个对象的编译类型和运行类型可以不一致
    父类的引用指向子类的对象 Animal an = new Dog(); an的编译类型是Animal,运行类型是Dog
    (2)编译类型在定义对象时就确定了,不能改变
    (3)运行类型时可以变化的
    an = new Cat(); an的运行类型变成了Cat,编译类型仍然是Animal
    (4)编译类型看定义时=的左边,运行类型看=的右边
  1. public class Poly{
  2. //animal编译类型就是Animal,运行类型Dog
  3. Animal animal = new Dog();
  4. animal.cry(); //狗叫
  5. //animal编译类型就是Animal,运行类型Cat
  6. animal = new Cat();
  7. animal.cry(); //猫叫
  8. }

多态的前提时两个对象/类存在继承关系

  • 向上转型
    (1)本质:父类的引用指向了子类的对象
    (2)语法:父类类型 引用名 = new 子类类型();
    (3)可以调用父类中的所有成员(遵循访问权限),不能调用子类中的特有成员
    编译阶段 调用哪些方法是编译类型决定的,比如animal想调用Cat类特有的catchMouse()方法编译报错
    运行阶段 调用是先从子类开始的,遵循方法调用规则
  • 向下转型
    子类类型 引用名 = (子类类型)父类引用;
    Cat cat = (Cat) animal;
    cat.catchMouse();
    只能强转父类的引用,不能强转父类的对象
    父类的引用必须指向的是当前目标类型的对象
    Animal animal = new Cat(); //此时的animal指向的是Cat
    Cat cat = (Cat) animal; //这样才能向下转型成Cat 如果是转成Dog类 不对
    向下转型后,可以调用子类类型中所有的成员
  • 属性不重写,属性的值看编译类型
  1. public class Poly01 {
  2. public static void main(String[] args) {
  3. //属性没有重写之说!属性的值看编译类型
  4. Base base = new Sub();//向上转型
  5. System.out.println(base.count);// ? 看编译类型 10
  6. Sub sub = new Sub();
  7. System.out.println(sub.count);//? 20
  8. }
  9. }
  10. class Base {
  11. //父类
  12. int count = 10;//属性
  13. }
  14. class Sub extends Base {
  15. //子类
  16. int count = 20;//属性 }
  17. }
  • instanceOf 比较操作符,用于判断对象的运行类型是否为 XX 类型或 XX 类型的子类型
  • 动态绑定机制
    当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定
    当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用
  1. public class DynamicBinding {
  2. public static void main(String[] args) {
  3. //a 的编译类型 A, 运行类型 B
  4. A a = new B();//向上转型
  5. System.out.println(a.sum());//?40 -> 30
  6. System.out.println(a.sum1());//?30-> 20
  7. }
  8. }
  9. class A {//父类
  10. public int i = 10;
  11. //动态绑定机制:
  12. public int sum() {//父类sum()
  13. return getI() + 10;//20 + 10
  14. }
  15. public int sum1() {//父类sum1()
  16. return i + 10;//10 + 10
  17. }
  18. public int getI() {//父类getI
  19. return i;
  20. }
  21. }
  22. class B extends A {//子类
  23. public int i = 20;
  24. // public int sum() {
  25. // return i + 20;
  26. // }
  27. public int getI() {//子类getI()
  28. return i;
  29. }
  30. // public int sum1() {
  31. // return i + 10;
  32. // }
  33. }
  • 多态数组
    数组的定义类型为父类类型,里面保存的实际元素为子类类型
  1. public class Person {
  2. private String name;
  3. private int age;
  4. public Person(String name, int age) {
  5. this.name = name;
  6. this.age = age;
  7. }
  8. public String getName() {
  9. return name;
  10. }
  11. public void setName(String name) {
  12. this.name = name;
  13. }
  14. public int getAge() {
  15. return age;
  16. }
  17. public void setAge(int age) {
  18. this.age = age;
  19. }
  20. public String say(){
  21. return name + '\t' + age;
  22. }
  23. }
  1. public class Student extends Person{
  2. private double score;
  3. public Student(String name, int age, double score) {
  4. super(name, age);
  5. this.score = score;
  6. }
  7. public double getScore() {
  8. return score;
  9. }
  10. public void setScore(double score) {
  11. this.score = score;
  12. }
  13. @Override
  14. public String say(){
  15. return super.say() + "score=" + score;
  16. }
  17. public void study(){
  18. System.out.println(getName()+"正在学习");
  19. }
  20. }
  1. public class Teacher extends Person{
  2. private double salary;
  3. public Teacher(String name, int age, double salary) {
  4. super(name, age);
  5. this.salary = salary;
  6. }
  7. public double getSalary() {
  8. return salary;
  9. }
  10. public void setSalary(double salary) {
  11. this.salary = salary;
  12. }
  13. @Override
  14. public String say(){
  15. return super.say()+"salary="+salary;
  16. }
  17. public void teach(){
  18. System.out.println(getName()+"正在讲课");
  19. }
  20. }
  1. public class PolyArray {
  2. public static void main(String[] args) {
  3. Person[] persons = new Person[5];
  4. persons[0] = new Person("jack",20);
  5. persons[1] = new Student("jack",18,100);
  6. persons[2] = new Student("smith",19,30.1);
  7. persons[3] = new Teacher("s",30,20000);
  8. persons[4] = new Teacher("king",50,25000);
  9. //循环遍历多态数组,调用say()
  10. for(int i = 0;i < persons.length;i++){
  11. System.out.println(persons[i].say());//动态绑定,编译类型是Person,运行类型根据实际情况
  12. if(persons[i] instanceof Student){ //运行类型是不是Student
  13. ((Student)persons[i]).study(); //向下转型
  14. } else if(persons[i] instanceof Teacher){
  15. Teacher teacher = (Teacher) persons[i];
  16. teacher.teach();
  17. } else if(persons[i] != null){
  18. System.out.println("");
  19. }else{
  20. System.out.println("类型有误");
  21. }
  22. }
  23. }
  24. }
  • 多态参数
    形参为父类类型,实参为子类类型
  1. public class Employee {
  2. private String name;
  3. private double salary;
  4. public Employee(String name, double salary) {
  5. this.name = name;
  6. this.salary = salary;
  7. }
  8. public double getAnnual() {
  9. return 12.0D * this.salary;
  10. }
  11. public String getName() {
  12. return this.name;
  13. }
  14. public void setName(String name) {
  15. this.name = name;
  16. }
  17. public double getSalary() {
  18. return this.salary;
  19. }
  20. public void setSalary(double salary) {
  21. this.salary = salary;
  22. }
  23. }
  1. public class Manager extends Employee {
  2. private double bonus;
  3. public Manager(String name, double salary, double bonus) {
  4. super(name, salary);
  5. this.bonus = bonus;
  6. }
  7. public double getBonus() {
  8. return this.bonus;
  9. }
  10. public void setBonus(double bonus) {
  11. this.bonus = bonus;
  12. }
  13. public void manage() {
  14. System.out.println("经理 " + this.getName() + " is managing");
  15. }
  16. public double getAnnual() {
  17. return super.getAnnual() + this.bonus;
  18. }
  19. }
  1. public class Worker extends Employee {
  2. public Worker(String name, double salary) {
  3. super(name, salary);
  4. }
  5. public void work() {
  6. System.out.println("普通员工 " + this.getName() + " is working");
  7. }
  8. public double getAnnual() {
  9. return super.getAnnual();
  10. }
  11. }
  1. public class PloyParameter {
  2. public PloyParameter() {
  3. }
  4. public static void main(String[] args) {
  5. Worker tom = new Worker("tom", 2500.0D);
  6. Manager milan = new Manager("milan", 5000.0D, 200000.0D);
  7. PloyParameter ployParameter = new PloyParameter();
  8. ployParameter.showEmpAnnual(tom);
  9. ployParameter.showEmpAnnual(milan);
  10. ployParameter.testWork(tom);
  11. ployParameter.testWork(milan);
  12. }
  13. public void showEmpAnnual(Employee e) {
  14. System.out.println(e.getAnnual());
  15. }
  16. public void testWork(Employee e) {
  17. if (e instanceof Worker) {
  18. ((Worker)e).work();
  19. } else if (e instanceof Manager) {
  20. ((Manager)e).manage();
  21. } else {
  22. System.out.println("");
  23. }
  24. }
  25. }
  • Object类
    是类层次结构的根类,每个类都使用Object作为超类,所有对象都实现这个类的方法。

    1. equals方法
      ==和equals的对比:
      ==是一个比较运算符,既可以判断基本类型,也可以判断引用类型。基本类型:值是否相等,引用类型:判断地址是否相等。
      equals只能判断引用类型,默认判断地址是否相等,子类中往往重写该方法,判断内容是否相等,如String类型。

      1. //Object类的equals方法
      2. public boolean equals(Object obj){
      3. return (this == obj); // 比较对象地址是否相同
      4. }
      5. //Integer类的equals方法
      6. public boolean equals(Object obj){
      7. if(obj instanceof Integer){
      8. return value == ((Integer)obj).intValue();
      9. }
      10. return false;
      11. }
      1. Integer i1 = new Integer(1000);
      2. Integer i1 = new Integer(1000);
      3. System.out.println(i1 == i2); // false
      4. System.out.println(i1.equals(i2)); //true
    2. hashCode()
      提高具有哈希结构的容器的效率
      两个引用,指向同一个对象,哈希值一样
      两个引用,指向不同对象,哈希值不一样
      哈希值主要依据地址号,不能完全将哈希值等价于地址
      ```java public class HashCode_ { public static void main(String[] args) { A a = new A(); A a1 = new A(); A a2 = a; System.out.println(a.hashCode()); //1163157884 System.out.println(a1.hashCode()); //1956725890 System.out.println(a2.hashCode()); //1163157884 } }

class A {}

  1. 3. toString()<br />默认返回:全类名+@+哈希值的十六进制 子类往往重写toString方法,用于返回对象的属性信息 <br />重写toString方法,打印对象或拼接对象时,都会自动调用该对象的toString形式 <br />直接输出一个对象时,toString方法会被默认的调用
  2. ```java
  3. public class ToString_ {
  4. public static void main(String[] args) {
  5. /*
  6. Object的toSting() 源码
  7. 1. getClass().getName() 类的全类名(包名+类名)
  8. 2. Integer.toHexString(hashCode()) 将对象的hashCode值转成16进制字符串
  9. public String toString() {
  10. return getClass().getName() + "@" + Integer.toHexString(hashCode());
  11. }
  12. */
  13. M m = new M("小妖怪", "巡山", 1000);
  14. System.out.println(m.toString()+" hashCode="+m.hashCode());
  15. // object.M@4554617c hashCode=1163157884
  16. }
  17. }
  18. class M{
  19. private String name;
  20. private String job;
  21. private double sal;
  22. public M(String name, String job, double sal) {
  23. this.name = name;
  24. this.job = job;
  25. this.sal = sal;
  26. }
  27. public String getName() {
  28. return name;
  29. }
  30. public void setName(String name) {
  31. this.name = name;
  32. }
  33. public String getJob() {
  34. return job;
  35. }
  36. public void setJob(String job) {
  37. this.job = job;
  38. }
  39. public double getSal() {
  40. return sal;
  41. }
  42. public void setSal(double sal) {
  43. this.sal = sal;
  44. }
  45. }
  1. // 重写toString方法,输出对象的属性
  2. // 使用快捷键即可 alt+insert -> toString
  3. @Override
  4. public String toString() { // 重写后,默认把对象的属性输出
  5. return "M{" +
  6. "name='" + name + '\'' +
  7. ", job='" + job + '\'' +
  8. ", sal=" + sal +
  9. '}';
  10. }
  1. M m = new M("小妖怪", "巡山", 1000);
  2. System.out.println(m.toString()); // M{name='小妖怪', job='巡山', sal=1000.0}
  1. System.out.println("当直接输出一个对象时,toString方法会被默认调用");
  2. System.out.println(m); // 等价 m.toString()
  3. /*
  4. 当直接输出一个对象时,toString方法会被默认调用
  5. M{name='小妖怪', job='巡山', sal=1000.0}
  6. */
  1. finalize()
    当对象被回收时,系统自动调用该对象的finalize方法。子类可以重写该方法,释放资源
    什么时候被回收:当某个对象没有任何引用时。jvm认为这个对象是一个垃圾对象,使用垃圾回收机制销毁该对象,销毁该对象前,先调用finalize方法
    垃圾回收机制的调用,是由系统决定,也可以通过System.gc()主动触发垃圾回收机制

    1. public class Finalize_ {
    2. public static void main(String[] args) {
    3. Car c = new Car("车");
    4. // 这时,car对象就是一个垃圾,回收器会回收对象,在销毁对象前,会调用该对象的finalize方法
    5. // 在finalize中,写自己的业务逻辑,比如释放资源:数据库连接,或打开文件..
    6. // 如果不重写,就会调用Object类的finalize,即默认处理
    7. c = null;
    8. System.gc();// 主动调用垃圾回收机制
    9. System.out.println("退出");
    10. }
    11. }
    12. class Car {
    13. private String name;
    14. public Car(String name) {
    15. this.name = name;
    16. }
    17. // 重写finalize
    18. @Override
    19. protected void finalize() throws Throwable {
    20. System.out.println("销毁");
    21. }
    22. }