【Java笔记】09 面向对象
一、类与对象

- 属性
成员属性=属性=field 访问修饰符 属性类型 属性名;
访问修饰符:public protected 默认 private
属性不赋值有默认值,跟数组默认值相同 - 方法(成员方法)
当程序执行到方法时,开辟一个独立的栈空间;
执行完毕,或者到return语句时,返回;
返回到调用方法的语句,继续执行后面的代码
方法的定义:
访问修饰符 返回数据类型 方法名(形参列表..){//方法体
语句;
return 返回值;
}
同一个类中的方法调用,直接调用即可,不需要创建对象
class A{//同一个类中的方法调用,直接调用即可,不需要创建对象pubilc void print(int n){System.out.println("输出");}public void test(){print();}}
跨类中的方法调用:A类调用B类方法,需要通过对象名调用。即创建B类的对象,再调用方法
二、方法重载
java中允许同一个类中,多个同名方法的存在,但要求形参列表不一致
方法名:必须相同
形参列表:必须不同(形参类型或个数或顺序,至少有一样不同,参数名无要求)
返回类型:无要求
三、可变参数
java允许将同一个类中多个同名同功能但参数个数不同的方法,封装成一个方法
可变参数的实参可以为0个或任意多个
可变参数的实参可以为数组
可变参数的本质就是数组
可变参数可以和普通类型的参数一起放在形参列表,但必须保证可变参数在最后
一个形参列表中最多只能出现一个可变参数
public class VarParameter{public static void main(String[] args){//求2个、3个、4个...参数的和HspMethod m = new HspMethod();System.out.println(m.sum(1,5,100));System.out.println(m.sum(1,19));}}class HspMethod{//int...表示接受的是可变参数,类型是int,即可接收多个int(0-多)//使用可变参数时,可以当做数组来使用,即nums可以当做数组//遍历nums求和即可public int sum(int... nums){int res = 0;for(int i =0;i < nums.length;i++){res += nums[i];}return res;}}
四、作用域
1.java中,主要的变量就是属性(成员变量)和局部变量
2.局部变量一般是指在成员方法中定义的变量
3.Java作用域分类
全局变量:即属性,作用域为整个类
局部变量:除了属性以外的其他变量,作用域为定义它的代码块中
4.属性可以不赋值,直接使用,因为有默认值,局部变量必须赋值后才能使用,没有默认值
5.作用域范围
全局变量/属性:可以被本类使用,或其他类使用(通过对象调用)
局部变量:只能在本类中对于的方法中使用
6.全局变量可以加修饰符,局部变量不能加修饰符
五、构造方法/构造器
构造方法又叫构造器,是类的一种特殊的方法,主要作用是完成对新对象的初始化
方法名和类名相同
没有返回值
创建对象时,系统自动的调用该类的构造器完成对象的初始化
[修饰符] 方法名(形参列表){
方法体;
}
1.构造器的修饰符可以默认,也可以时public protected private
2.构造器没有返回值
3.方法名和类名字必须一样
4.参数列表和成员方法规则一样
5.一个类可以定义多个不同的构造器,即构造器重载
6.定义了自己的构造器,默认构造器就覆盖了,除非显式定义一下
public class Constructor{public static void main(String[] args){//new一个对象时,直接通过构造器初始化Person p1 = new Person("jack",3);}}class Person{String name;int age;public Person(String pName,int pAge){name = pName;age = pAge;}}
六、this
java虚拟机给每个对象分配this,代表当前对象
this 关键字可以用来访问本类的属性、方法、构造器
this 用于区分当前类的属性和局部变量
访问成员方法的语法:this.方法名(参数列表);
class T{public void f1(){System.out.println("f1()");}public void f2(){System.out.println("f2()");//调用本类的f1//第一种方法f1();//第二种方法this.f1();}}
访问构造器语法:this(参数列表); 注意只能在构造器中使用(即只能在构造器中访问另外一个构造器, 必须放在第一 条语句)
class T{public T(){this("jack",20);//访问构造器语法:this(参数列表);必须放置第一条语句System.out.println("T()构造器");}public T(String name,int age){System.out.println("T(String name,int age)构造器");}}
this 不能在类定义的外部使用,只能在类定义的方法中使用
七、访问修饰符
公开 public
受保护 protected 对子类和同一包的类公开
默认 对同一个包的类公开
私有 private 不对外公开
只有默认和public可以修饰类
八、封装
把抽象出的数据[属性]和对数据的操作[方法]封装在一起,数据被保护在内部,程序的其他部分只有通过被授权的操作[方法],才能对数据进行操作。
步骤:
1.先对属性私有化,让外部不能直接修改属性
2.提供一个公共的set方法,用于对属性判断并赋值
public void setXxx(类型 参数名){//验证属性 = 参数名;}
3.提供一个公共的get方法,用于获取属性的值
public getXxx(){//权限判断return xx;}
- 有构造器时想使用set方法,在构造器中使用set
public Person(String name, int age, double salary) {// this.name = name;// this.age = age;// this.salary = salary;setSalary(salary);setAge(age);setName(name);}
九、继承
多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类(基类,超类),在父类中定义这些相同的属性和方法,子类(派生类)通过extends声明继承父类
子类继承了所有的属性和方法,但私有属性和方法不能在子类直接访问,要通过父类公共的方法;
创建子类时,不管使用子类的哪个构造器,默认都会调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用super指定使用父类的哪个构造器,即在子类构造器中写super(对应参数列表);
super()使用时放在构造器的第一行,super只能在构造器中使用;
super()和this()都只能放在第一行->不能同时存在;
java所有类都是Object类的子类,Object是所有类的基类;
父类构造器的调用不限于父类,而是一直往上追溯;
java单继承,子类最多只能直接继承一个父类;
子类和父类之间要满足is-a关系;
public class Encap {public static void main(String[] args) {Son son = new Son();//内存的布局// 要按照查找关系来返回信息// (1) 首先看子类是否有该属性// (2) 如果子类有这个属性,并且可以访问,则返回信息// (3) 如果子类没有这个属性,就看父类有没有这个属性(如果父类有该属性,并且可以访问,就返回信息..)// (4) 如果父类没有就按照(3)的规则,继续找上级父类,直到 Object...System.out.println(son.name);//返回就是大头儿子//System.out.println(son.age);//会报错,age是私有的属性System.out.println(son.getAge());//返回的就是 39System.out.println(son.hobby);//返回的就是旅游}}class GrandPa {//爷类String name = "大头爷爷";String hobby = "旅游";}class Father extends GrandPa {//父类String name = "大头爸爸";private int age = 39;public int getAge() {return age;}}class Son extends Father { //子类String name = "大头儿子";}
十、super关键字
访问父类的属性、方法、构造器:
1.访问属性(不能访问父类的private属性)
super.属性名;
2.访问父类的方法,不能访问父类的private方法
super.方法名(参数列表);
3.访问父类的构造器
super(参数列表); 只能放在第一句
子类、父类方法有重名时,为了访问父类的成员,用super。没有重名,super,this,直接访问一样
找方法时,顺序:
1.先找本类,如果有,调用
2.如果没有,则找父类。父类如果有,并可以调用,则调用
3.如果父类没有,继续找父类的父类,知道Object
查找方法过程中,找到了但不能访问(比如private),报错
十一、方法重写/覆盖
子类有一个方法和父类的某个方法的名称、返回类型、参数一样,子类的这个方法覆盖了父类的方法
返回类型父类的返回类型是子类的父类也可以。比如父类返回类型是Object,子类是String
子类方法不能缩小父类方法的访问权限
- 比较方法重载和重写 | 名称 | 发生范围 | 方法名 | 形参列表 | 返回类型 | 修饰符 | | —- | —- | —- | —- | —- | —- | | 重载 | 本类 | 必须一样 | 类型,个数或顺序至少有一个不同 | 无要求 | 无要求 | | 重写 | 父子类 | 必须一样 | 相同 | 子类重写的方法,返回类型和父类的返回类型一直或者是其子类 | 子类方法不能缩小父类方法的访问范围 |
多态
问题:代码复用性不高
解决方案:多态—>方法或对象有多种形态
- 方法的多态
方法重载、重写体现多态 - 对象的多态
(1)一个对象的编译类型和运行类型可以不一致
父类的引用指向子类的对象 Animal an = new Dog(); an的编译类型是Animal,运行类型是Dog
(2)编译类型在定义对象时就确定了,不能改变
(3)运行类型时可以变化的
an = new Cat(); an的运行类型变成了Cat,编译类型仍然是Animal
(4)编译类型看定义时=的左边,运行类型看=的右边
public class Poly{//animal编译类型就是Animal,运行类型DogAnimal animal = new Dog();animal.cry(); //狗叫//animal编译类型就是Animal,运行类型Catanimal = new Cat();animal.cry(); //猫叫}
多态的前提时两个对象/类存在继承关系
- 向上转型
(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类 不对
向下转型后,可以调用子类类型中所有的成员 - 属性不重写,属性的值看编译类型
public class Poly01 {public static void main(String[] args) {//属性没有重写之说!属性的值看编译类型Base base = new Sub();//向上转型System.out.println(base.count);// ? 看编译类型 10Sub sub = new Sub();System.out.println(sub.count);//? 20}}class Base {//父类int count = 10;//属性}class Sub extends Base {//子类int count = 20;//属性 }}
- instanceOf 比较操作符,用于判断对象的运行类型是否为 XX 类型或 XX 类型的子类型
- 动态绑定机制
当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定
当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用
public class DynamicBinding {public static void main(String[] args) {//a 的编译类型 A, 运行类型 BA a = new B();//向上转型System.out.println(a.sum());//?40 -> 30System.out.println(a.sum1());//?30-> 20}}class A {//父类public int i = 10;//动态绑定机制:public int sum() {//父类sum()return getI() + 10;//20 + 10}public int sum1() {//父类sum1()return i + 10;//10 + 10}public int getI() {//父类getIreturn i;}}class B extends A {//子类public int i = 20;// public int sum() {// return i + 20;// }public int getI() {//子类getI()return i;}// public int sum1() {// return i + 10;// }}
- 多态数组
数组的定义类型为父类类型,里面保存的实际元素为子类类型
public class Person {private String name;private int age;public Person(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String say(){return name + '\t' + age;}}
public class Student extends Person{private double score;public Student(String name, int age, double score) {super(name, age);this.score = score;}public double getScore() {return score;}public void setScore(double score) {this.score = score;}@Overridepublic String say(){return super.say() + "score=" + score;}public void study(){System.out.println(getName()+"正在学习");}}
public class Teacher extends Person{private double salary;public Teacher(String name, int age, double salary) {super(name, age);this.salary = salary;}public double getSalary() {return salary;}public void setSalary(double salary) {this.salary = salary;}@Overridepublic String say(){return super.say()+"salary="+salary;}public void teach(){System.out.println(getName()+"正在讲课");}}
public class PolyArray {public static void main(String[] args) {Person[] persons = new Person[5];persons[0] = new Person("jack",20);persons[1] = new Student("jack",18,100);persons[2] = new Student("smith",19,30.1);persons[3] = new Teacher("s",30,20000);persons[4] = new Teacher("king",50,25000);//循环遍历多态数组,调用say()for(int i = 0;i < persons.length;i++){System.out.println(persons[i].say());//动态绑定,编译类型是Person,运行类型根据实际情况if(persons[i] instanceof Student){ //运行类型是不是Student((Student)persons[i]).study(); //向下转型} else if(persons[i] instanceof Teacher){Teacher teacher = (Teacher) persons[i];teacher.teach();} else if(persons[i] != null){System.out.println("");}else{System.out.println("类型有误");}}}}
- 多态参数
形参为父类类型,实参为子类类型
public class Employee {private String name;private double salary;public Employee(String name, double salary) {this.name = name;this.salary = salary;}public double getAnnual() {return 12.0D * this.salary;}public String getName() {return this.name;}public void setName(String name) {this.name = name;}public double getSalary() {return this.salary;}public void setSalary(double salary) {this.salary = salary;}}
public class Manager extends Employee {private double bonus;public Manager(String name, double salary, double bonus) {super(name, salary);this.bonus = bonus;}public double getBonus() {return this.bonus;}public void setBonus(double bonus) {this.bonus = bonus;}public void manage() {System.out.println("经理 " + this.getName() + " is managing");}public double getAnnual() {return super.getAnnual() + this.bonus;}}
public class Worker extends Employee {public Worker(String name, double salary) {super(name, salary);}public void work() {System.out.println("普通员工 " + this.getName() + " is working");}public double getAnnual() {return super.getAnnual();}}
public class PloyParameter {public PloyParameter() {}public static void main(String[] args) {Worker tom = new Worker("tom", 2500.0D);Manager milan = new Manager("milan", 5000.0D, 200000.0D);PloyParameter ployParameter = new PloyParameter();ployParameter.showEmpAnnual(tom);ployParameter.showEmpAnnual(milan);ployParameter.testWork(tom);ployParameter.testWork(milan);}public void showEmpAnnual(Employee e) {System.out.println(e.getAnnual());}public void testWork(Employee e) {if (e instanceof Worker) {((Worker)e).work();} else if (e instanceof Manager) {((Manager)e).manage();} else {System.out.println("");}}}
Object类
是类层次结构的根类,每个类都使用Object作为超类,所有对象都实现这个类的方法。equals方法
==和equals的对比:
==是一个比较运算符,既可以判断基本类型,也可以判断引用类型。基本类型:值是否相等,引用类型:判断地址是否相等。
equals只能判断引用类型,默认判断地址是否相等,子类中往往重写该方法,判断内容是否相等,如String类型。//Object类的equals方法public boolean equals(Object obj){return (this == obj); // 比较对象地址是否相同}//Integer类的equals方法public boolean equals(Object obj){if(obj instanceof Integer){return value == ((Integer)obj).intValue();}return false;}
Integer i1 = new Integer(1000);Integer i1 = new Integer(1000);System.out.println(i1 == i2); // falseSystem.out.println(i1.equals(i2)); //true
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 {}
3. toString()<br />默认返回:全类名+@+哈希值的十六进制 子类往往重写toString方法,用于返回对象的属性信息 <br />重写toString方法,打印对象或拼接对象时,都会自动调用该对象的toString形式 <br />直接输出一个对象时,toString方法会被默认的调用```javapublic class ToString_ {public static void main(String[] args) {/*Object的toSting() 源码1. getClass().getName() 类的全类名(包名+类名)2. Integer.toHexString(hashCode()) 将对象的hashCode值转成16进制字符串public String toString() {return getClass().getName() + "@" + Integer.toHexString(hashCode());}*/M m = new M("小妖怪", "巡山", 1000);System.out.println(m.toString()+" hashCode="+m.hashCode());// object.M@4554617c hashCode=1163157884}}class M{private String name;private String job;private double sal;public M(String name, String job, double sal) {this.name = name;this.job = job;this.sal = sal;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getJob() {return job;}public void setJob(String job) {this.job = job;}public double getSal() {return sal;}public void setSal(double sal) {this.sal = sal;}}
// 重写toString方法,输出对象的属性// 使用快捷键即可 alt+insert -> toString@Overridepublic String toString() { // 重写后,默认把对象的属性输出return "M{" +"name='" + name + '\'' +", job='" + job + '\'' +", sal=" + sal +'}';}
M m = new M("小妖怪", "巡山", 1000);System.out.println(m.toString()); // M{name='小妖怪', job='巡山', sal=1000.0}
System.out.println("当直接输出一个对象时,toString方法会被默认调用");System.out.println(m); // 等价 m.toString()/*当直接输出一个对象时,toString方法会被默认调用M{name='小妖怪', job='巡山', sal=1000.0}*/
finalize()
当对象被回收时,系统自动调用该对象的finalize方法。子类可以重写该方法,释放资源
什么时候被回收:当某个对象没有任何引用时。jvm认为这个对象是一个垃圾对象,使用垃圾回收机制销毁该对象,销毁该对象前,先调用finalize方法
垃圾回收机制的调用,是由系统决定,也可以通过System.gc()主动触发垃圾回收机制public class Finalize_ {public static void main(String[] args) {Car c = new Car("车");// 这时,car对象就是一个垃圾,回收器会回收对象,在销毁对象前,会调用该对象的finalize方法// 在finalize中,写自己的业务逻辑,比如释放资源:数据库连接,或打开文件..// 如果不重写,就会调用Object类的finalize,即默认处理c = null;System.gc();// 主动调用垃圾回收机制System.out.println("退出");}}class Car {private String name;public Car(String name) {this.name = name;}// 重写finalize@Overrideprotected void finalize() throws Throwable {System.out.println("销毁");}}
