赤赤赤赤辰

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

面向对象(中)

浓缩

继承性

  • 面向对象特征之二:继承性

    1. 为什么要有类的继承性?
    • 减少代码冗余,提高代码复用性
    • 便于功能拓展
    • 为了之后的多态性使用提供了前提
    1. 继承性的格式
    class A extends B{}
    A:子类,派生类,subclass
    B:父类,超类,基类,superclass
    
    1. 子类继承父类以后有哪些不同?
    • 体现:一旦子类A继承父类B以后,子类A就获取了父类B中所证明的所有属性和方法。特别的,父类中声明为private的属性或方法,子类继承父类以后,仍然认为获取了父类中的私有结构,只是因为封装性的影响,使得子类不能直接调用父类的结构而已
    • 子类继承父类以后,还可以声明自己特有的属性或方法,实现功能的拓展
    1. Java中继承性的说明
    • 子类继承父类以后,就获取了直接父类以及所间接父类中声明的属性和方法

重写

- 1. 重写:子类继承父类以后,可以对父类中同名同参数的方法进行覆盖操作
- 2. 应用:重写以后,当子类创建对象以后,通过子类对象调用父类中的同名参数方法时,实际执行的是子类重写父类的方法
- 3. 重写的规定
     - 方法声明:权限修饰符 返回值类型 方法名(形参列表) throws 异常的类型{
       }
     - 子类重写的方法的权限修饰符不小于父类被重写方法的权限修饰符
       - 特殊情况:子类不能重写父类申明为private的权限的方法
     - 返回值类型:
       - 父类被重写的方法的返回值类型是void,则子类重写的也只能返回void
       - 父类被重写的方法的返回值是A类型,则子类重写的方法的返回值是A或者A的子类
       - 父类被重写的方法的返回值是基本数据类型(double,int...),则子类重写的方法的返回值类型必须是相同的
     - 子类重写方法的异常类型不大于父类被重写的方法抛出的异常类型
- 子类和父类中的同名参数的方法要么都声明为非static的(考虑充血),要么都声明为非static的(不是重写)
- 开发中一般写为同名
    1. 什么是方法的重写(override/overwrite)
    • 子类继承父类以后,可以对父类中同参数的方法,进行覆盖操作。
    • 重载(Overloading):是指在一个类中可以有多个方明名字相同单参数列表不同的不同方法
    • 重写(Override):子类继承父类时, 可以覆盖原来父类里面的方法。这个方法和父类的方法名称和参数列表必须完全一致,否则就不叫重写。注意:final修饰的方法不可以重写
    1. 应用
    • 重写以后,当创建子类对象以后,通过子类对象调用父类中的同名同参数的方法时,实际执行的是子类重写父类的方法。
    1. 举例
    • class Circle{
          public double findArea(double r){}//求面积
      }
      class Cylinder extends Circle{
          public double findArea(double r){}
      }
      
    1. 重写的规则
    • 方法声明:权限修饰符 返回值类型 方法名(形参列表) throws 异常的类型{
      }
    • 子类重写的方法的权限修饰符不小于父类被重写方法的权限修饰符
      • 特殊情况:子类不能重写父类申明为private的权限的方法
    • 返回值类型:
      • 父类被重写的方法的返回值类型是void,则子类重写的也只能返回void
      • 父类被重写的方法的返回值是A类型,则子类重写的方法的返回值是A或者A的子类
      • 父类被重写的方法的返回值是基本数据类型(double,int...),则子类重写的方法的返回值类型必须是相同的
    • 子类重写方法的异常类型不大于父类被重写的方法抛出的异常类型
    1. 区分重载和重写?
    • 二者概念

    • 重载:一个类中有多个函数名相同,参数列表不同的方法,彼此之间构成重载。构造器也可以重载。

    • 重写:子类继承父类以后,可以对父类中同名同参数的方法,进行覆盖操作。

    • 重载和重写的具体规则

      • 重写规则见上方,重载规则见上章
    • 重载不表现为多态性:对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法

    • 重写表现为多态性

    • 重载,是指允许存在多个同名方法,而这些方法的参数不同。编译器根据方法不同的参数表,对同名方法的名称做修饰。对于编译器而言,这些同名方法就成了 不同的方法。它们的调用地址在编译期就绑定了。Java的重载是可以包括父类和子类的,即子类可以重载父类的同名不同参数的方法。

      所以:对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法, 这称为“早绑定”或“静态绑定”;

      而对于多态,只有等到方法调用的那一刻,解释运行器才会确定所要调用的具体 方法,这称为“晚绑定”“动态绑定”

      引用一句Bruce Eckel的话:“不要犯傻,如果它不是晚绑定,它就不是多态。”

    1. 举例·
    Person.java
        package com.shc.exer2;
    
    public class Person {
    	String name;
    	private void show() {
    		System.out.println("Person show");
    	}
    	void eat() {
    		System.out.println("Person eat");
    	}
    	public void walk() {
    		System.out.println("Person walk");
    //		eat(); //调用的Student类的eat方法(重写)
    		show(); //调用Person类的show(由于private 所以不构成重写)
    	}
    }
    
    • Student.java
          package com.shc.exer2;
      
      public class Student extends Person{
      	String major;
      	
      	public Student(String major) {
      		super();
      		this.major=major;
      	}
      	
      	public void study() {
      		System.out.println("Student 专业是:"+major);
      	}
      	
      	//对父类的eat()(default)进行了重写
      	//重写的权限必须大于等于父类的该方法权限
      	//三角号:重写
      	public void eat() {
      		System.out.println("Student eat");		
      	}
      	
      	//子类不能重写父类的private方法
      	//因为子类根本看不到哪个方法 自然也就无从知道是不是重写
      	public void show() {
      		System.out.println("Student show");
      	}
      }
      
      
    • Test.java
          package com.shc.exer2;
      
      public class StudentTest {
      	public static void main(String[] args) {
      		Student student = new Student();
              //student对象调用父类的walk方法(在加载时Student类型引用变量已经得到了父类的属性和方法)
      		student.walk();
      		System.out.println("***************");
      		Person person = new Person(); //加载时 Person对引用变量获得Person的全部属性和方法 由于Student是子类 所以并没有得到子类的属性和方法 自然也没有得到Student的eat方法 所以只能调用person自己的eat()方法
      		person.eat();
      	}
      }
      
    1. 测试四种权限不同的修饰符
    • image

      package com.shc.exer3;
      /*
       * 体会4种不同的权限修饰
       */
      public class Order {
      	private int orderPrivate;
      	int orderDefult;
      	protected int orderProtected;
      	public int orderPublic;
      	
      	private void methodPrivate() {}
      	void methodDefault() {}
      	protected void methodProtected() {}
      	public void methodPublic() {}
      }
      
      
      package com.shc.exer3;
      
      public class OrderTest {
      	public static void main(String[] args) {
      		Order order = new Order();
      		
      		order.orderDefult = 1;
      		order.orderProtected = 2;
      		order.orderPublic = 3;
      		
      		order.methodDefault();
      		order.methodProtected();
      		order.methodPublic();
      		
      		//同一个包中不同类,不可调用Order类中私有的属性和方法
      //		order.orderPrivate = 0;
      //		order.methodPrivate();
      	}
      }
      
      
      package com.shc.exer4;
      
      import com.shc.exer3.Order;
      
      public class OrderTest {
      	public static void main(String[] args) {
      		Order order = new Order();
      		order.orderPublic = 1;
      		order.methodPublic();
      		
      		//不同包的普通类(非子类)要使用Order类,不可以调用private protected 缺省
      //		order.orderPrivate = 2;
      //		order.orderDefault = 3;
      //		order.orderProtected = 4;
      //		
      //		order.methodPrivate();
      //		order.methodDefault();
      //		order.methodProtected();
      	}
      }
      
      
      package com.shc.exer4;
      
      import com.shc.exer3.Order;
      
      public class SubOder extends Order {
      	
      	public void mehtod() {
      		orderProtected = 1;
      		orderPublic = 2;
      		methodProtected();
      		methodPublic();
      		
      //		orderDefault=1;
      //		orderPrivate=2;
      //		不同包的**子类**中 不能调用Order类中声明为private,default的
      //		methodDefault();
      //		methodPrivate();
      	}
      }
      
      

super

    1. super可以理解为:父类的
    1. 可以用来调的结构
    • 属性、方法、构造器
    1. super调用属性、方法
    /*
     * super关键字的使用
     * 1. super理解为父类的
     * 2. super可以用来调属性 构造器 方法
     * 3. super的使用 super.
     *   3.1 可以在子类方法或者构造器中,通过"使用super.属性"或"super.方法"的方式,现实的调用父类中声明的属性或者方法,但是,通常情况下,我们习惯上省略"super.".
     * 	 3.2 特殊情况:当子类和父类定义了同名的属性时,我们想在子类中调用父类生命的属性,必须显示的调用"super.属性"
     *   	3.2.1 通常开发中子父类不会同名属性  
     *   3.3 特殊情况:子类重写了父类的方法,想在子类中调用父类的被重写的方法,则使用"super.方法"
     *   3.4 子类的方法可以覆盖父类的方法 但是子类的属性不会覆盖父类的属性
     *   3.5 super.先到直接父类中找 找不到再去间接父类中找
     *   3.6 如果子类没有覆盖父类的某个方法或者属性 则在子类中"this.该属性或者方法"也可以调用到父类的属性或方法. 
     *   this./super.可以理解成先到哪个类中去找 找不到在逐层向上找
     *
    
     */
    
    1. super调用构造器
    •  /* 4.super调用构造器 super()
       * 	 4.1 我们可以在子类的构造器中显示的使用"super(参数列表)"的方式,调用父类中声明的指定构造器
       * 	 4.2 "super(形参列表)"的使用,必须声明在子类构造器的首行!
       * 	 4.3 我们在一个类的构造器中,针对于"this(参列表)"或super(形参列表)"二选一(因为this(形参列表)也必须在构造器首行) 
       *   4.4 如果子类中不显式调用super(形参列表),则默认super().!!!!!这时如果父类中没有空参构造器则会报错.!!常见错误!!!!
       *   	4.4.1 子类不显示写构造器时--系统默认给空参构造器--构造器第一行默认调用super()父类空参构造器--父类没有空参构造器--error
       *   4.5 所以构造器的首行不是super()就是this(). 一个类有n个构造器,则构造器的最多调用n个this 至少有一个调用super(形参列表),调用了父类构造器
       */
      

子类对象实例化全过程

    1. 从结果上看:继承性
    • /*
       * 子类对象实例化过程
       * 
       * 1. 从结构上看:(继承性):
       * 	子类继承父类以后,就获取了父类中声明的属性或者方法
       *  创建子类的对象,在堆空间中,就会加载所有父类中声明的属性
      /
      
    1. 从过程上看:
    • /* 
       * 2. 从过程上看
       *  当我们通过子类的构造器创建子类对象时,我们一定会直接或者间接的调用父类的构造器,进而调用父类的父类的构造器
       *  直到调用了java.lang.Object类中空参的构造器中为止.正因为加载过所有父类的结构,所以才可以看到内存中父类的结构,子类对象才可以将父类的属性,方法进行调用
       *  明确:虽然创建子类对象时调用了父类的构造器,但是自始至终就创建过一个对象,即为new的子类对象
       */
      
    • 图示

    • 注意是先产生对象并对成员变量默认初始化 ,再进行构造方法

        1. 产生对象并对成员变量默认初始化
        2. 对构造方法中的形式参数赋值
        3. 构造方法中是否有this语句调用
        4. super语句调用或隐式调用父类的构造方法
        5. 对当前父类的成员变量显式初始化
        6. 执行当前构造方法中的代码
    • super()和this()不同时在一个构造器中出现 ---- 因为super()和this()都必须位于构造器中第一句

    • super()和this()为什么只能在构造器的第一句出现?

      • 无论通过那个构造去创建子类对象,需要保证先初始化父类
      • 目的:当前子类继承父类以后,"继承"父类中所有的属性和方法,因此子类有必要知道父类如何对对象进行初始化

多态性

  • 面向对象特征之三:多态性

    1. 多态性的理解 : 可以理解为一个事物的多种形态
    1. 何为多态性 : 对象的多态性:父类的引用指向子类的对象
    1. 多态性的使用:虚拟方法的调用
    • 有了对象的多态性以后,我们在编译时,只能调用父类中声明的方法,但是在运行期间,我们实际执行的是子类重写父类的方法

    • 总结:编译看左,运行看右

    1. 多态性的使用前提
    • (1) 类的继承关系
    • (2) 子类方法的重写。意义:省的设计多个重复函数
    1. 举例
    • 多态性的使用:当父类引用调用子父类同名参数的方法时,实际执行的是子类重写父类的方法---虚拟方法的调用

      public class PersonTest {
      	public static void main(String[] args) {
      		//对象的多态性:父类的引用指向子 类的对象
      		Person p2 = new Man();
      		//多态性的使用:当父类引用调用子父类同名参数的方法时,实际执行的是子类重写父类的方法---虚拟方法的调用
      		p2.eat();
      		p2.walk();
      //		p2.earnMoney(); //不能通过父类引用调用子类特有的方法
      	}
      }
      
      class Person {
      	String name;
      	int age;
      	public void eat() {
      		System.out.println("People eat");
      	}
      	
      	public void walk() {
      		System.out.println("People walk");
      	}
      }
      
      class Man extends Person{
      	boolean isSmoking;
      	public void earnMoney() {
      		System.out.println("Man earnmoney");
      	}	
      	@Override
      	public void eat() {
      		System.out.println("Man eat");
      	}
      	@Override
      	public void walk() {
      		System.out.println("Man walk");
      	}
      }
      
      class Woman extends Person{
      	boolean isBeauty;	
      	@Override
      	public void eat() {
      		System.out.println("Woman eat");
      	}
      	@Override
      	public void walk() {
      		System.out.println("Woman walk");
      	}
      }
      
    • package com.shc.exer6;
      
      import java.sql.Connection;
      
      /*
       * 多态性举例
       */
      //一
      public class AnimalTest {
      	
      	public static void main(String[] args) {
      		AnimalTest animalTest = new AnimalTest();
      		animalTest.func(new Animal());
      		System.out.println("**********");
      		animalTest.func(new Dog());
      		System.out.println("**********");
      		animalTest.func(new Cat());
      		
      	}
      	
      	public void func(Animal animal) {
      		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("Animal eat");
      	}
      	
      	public void shout() {
      		System.out.println("Animal shout");
      	}
      }
      
      class Dog extends Animal{
      	public void eat() {
      		System.out.println("Dog eat");
      	}
      	
      	public void shout() {
      		System.out.println("Dog shout");
      	}
      }
      
      class Cat extends Animal{
      	public void eat() {
      		System.out.println("Cat eat");
      	}
      	
      	public void shout() {
      		System.out.println("Cat shout");
      	}
      }
      
      //二
      class Order{
      	public void func(Object ob) {
      		
      	}
      }
      
      //三
      class Driver{
      	public void doData(Connection conn) { 
      		//conn = new MySOlConnection()
      		//coon = new OrcaleConnection()
      //		conn.method1();
      	}
      }
      
    1. 多态性使用注意
    • 只适用于方法,不适用于属性 ( 属性的编译和运行都看左边 )
    1. 关于向上转型向下转型
    • 向上转型:多态

    • 向下转型

      • 为什么使用向下转型?

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

        • (注意,只是子类特有的方法不能被父类引用变量调用,如果有一个方法是子类重写了父类的,那么父类引用变量调用该方法时,会调用子类重写了的方法)

      • 如何实现向下转型?

        • 使用强制类型转换符()
      • 使用时的注意点

          1. 使用强转时,可能出现ClassCastException异常
          2. 为了避免在向下转型时出现ClassException的异常, 我们呢在向下转型之前, 先进行instanceof判断, 如果返回true, 就进行向下转型。如果返回false,就不进行向下转型。
      • a instanceof A的使用

        • as
        • asd
        • 编译器要求a所属的类与类A必须是子类和父类的关系,否则编译错误
        • 如果a的A的子类的对象,那么也是返回true
  • 对多态性理解?

    • 实现代码通用性

    • Object类中定义的public boolean equals(Object obj){}

      JDBC: 使用java程序操作(获取数据库连接,CRUD)数据库(MySQL,Oracle,DB2,SQL Sever)

    • 抽象类、接口的使用肯定体现了多态性(抽象类,接口不能实例化)

多态性试题

package com.shc.test;
/*
 * 子类继承父类
  1.若子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的
	同名方法,系统将不可能把父类里的方法转移到子类中。编译看左 运行看右
  2.对于实例变量则不存在这样的现象,即使子类里定义了与父类完全相同的
	实例变量,这个实例变量依然不可能覆盖父类中定义的实例变量。编译运行都看左
 */
//开发中不要在子父类中定义同名的属性 
//方法必然会出现同名啊 重写嘛
class Base {
	int count = 10;
	public void display() {
		System.out.println(this.count);
	}
}

class Sub extends Base {
	int count = 20;
	public void display() {
		System.out.println(this.count);
	}
}

public class FieldMethodTest {
	public static void main(String[] args){
		Sub s = new Sub();
		System.out.println(s.count); //20 先在this中找count若没找到 再去super中找count
		s.display(); //20
		Base b = s; 
		System.out.println(b == s); //true
		System.out.println(b.count);//10 多态性对属性不适用 只对方法适用
		b.display(); //10 多态性 父类引用变量掌管子类对象,此时调用父类被重写的方法,执行的是子类的方法
	}
}

    1. 多态是编译时行为还是运行时行为?
    • 运行时行为

    • 在运行之前不知道答案

      package com.shc.test;
      import java.util.Random;
      
      //面试题:多态是编译时行为还是运行时行为?
      //证明如下:
      class Animal  {
      	protected void eat() {
      		System.out.println("animal eat food");
      	}
      }
      
      class Cat  extends Animal  {
      	protected void eat() {
      		System.out.println("cat eat fish");
      	}
      }
      
      class Dog  extends Animal  {
      	public void eat() {
      		System.out.println("Dog eat bone");
      	}
      }
      
      class Sheep  extends Animal  {
      	public void eat() {
      		System.out.println("Sheep eat grass");
          }
      }
      
      public class InterviewTest {
      
      	public static Animal  getInstance(int key) {
      		switch (key) {
      		case 0:
      			return new Cat ();
      		case 1:
      			return new Dog ();
      		default:
      			return new Sheep ();
      		}
      
      	}
      
      	public static void main(String[] args) {
      		int key = new Random().nextInt(3);
      		System.out.println(key);
      		Animal  animal = getInstance(key);
      		animal.eat();
      	}
      }
      
    • 0
      cat eat fish
      1
      Dog eat bone
      
    • package com.shc.test;
      //考查多态的笔试题目:
      public class InterviewTest1 {
      
      	public static void main(String[] args) {
      		Base base = new Sub();
      		base.add(1, 2, 3); //sub_1  
      		//子类重写父类方法 父类引用变量调用子类重写的方法
      		//public void add(int a, int[] arr) {}对public void add(int a, int... arr) {}构成重写
      		//public void add(int a, int b, int c){}不对public void add(int a, int... arr) {}构成重写
      		
      		Sub s = (Sub)base;
      		//调用最像的
      		s.add(1,2,3);
      		s.add(1,new int[] {2,3});
      		
      	}
      }
      
      class Base {
      	public void add(int a, int... arr) {
      		System.out.println("base");
      	}
      }
      
      class Sub extends Base {
      
      	public void add(int a, int[] arr) {
      		System.out.println("sub_1");
      	}
      
      	public void add(int a, int b, int c) {
      		System.out.println("sub_2");
      	}
      
      }
      
    • sub_1
      sub_2
      sub_1
      

Object类使用

    1. java.lang.Object类的说明
    • Object类是所有Java类的根父类
    • 如果在类的声明未使用extends指明父类,则默认父类为java.lang.Object
    • Object类中的功能(属性、方法)具有通用性
      • 属性:无

      • 方法:equals()/toString()/getClass()反射/hashCode()集合/clone()/finalize()/线程通信wait、notify()、notifyAll()

    • Object类中只声明了一个空参构造器
    • 数组也可以看作是一种继承于Object的特殊的类
    1. equals()方法
    • equals()方法的使用

        1. 是一个方法,而非运算符
        1. 只能适用于引用数据类型
        1. Object类中equals()定义
         *  public boolean equals(Object obj){
         *        return (this==obj);
         *    }
        
        1. 如String、Date、File、包装类等都重写了Object类中的equals()方法。
      • 重写以后,比较的不是两个引用地址是否相同,而是比较两个对象的实体内容是否相同。

        1. 通常情况下,我们自定义的类如果使用equals()的话,也通常是比较两个对象的实体内容是否相同,那么,我们就要对Object类中的equals()进行重写
      • 重写equals时,基本数据类型用==,引用数据类型用equals(要用重写过Object中的equals的equals

      • 开发中 通常直接alt+shift+s 直接自动生成

    • 手动重写举例

      • package com.shc.java;
        
        public class User {
        	String name;
        	int age;
        	@Override
        	//重写equals()方法
        	public boolean equals(Object obj) {
        		if(obj==this) return true;
        		if(obj instanceof User) {
        			User u = (User) obj;
        			return this.age ==u.age&&this.name.equals(u.name);
        		}
        		return false;
        	}
        }
        
    • 开发中自动生成

      • @Override
        	public boolean equals(Object obj) {
        		if (this == obj)
        			return true;
        		if (obj == null)
        			return false;
        		if (getClass() != obj.getClass()) //判断二者类是否一致
        			return false;
        		User other = (User) obj;
        		if (age != other.age)
        			return false;
        		if (name == null) {
        			if (other.name != null)
        				return false;
        		} else if (!name.equals(other.name))
        			return false;
        		return true;
        	}
        
    • 自己写的漏洞在哪里?

      • Man m = new Man("shc",12); Person p = new Person("shc",12);

      • 自己写的会导致m.equlals(p) == true

      • 举例

      • @Test
        	public void test2() {
        		Man m = new Man(12,"shc");
        		Person p = new Person(12,"shc");
        		System.out.println(p.equals(m));//true
        		System.out.println(m.equals(p));//false
        	}
        
        package com.shc.java3;
        
        public class Man extends Person{
        	
        	public Man(int age, String name) {
        		super(age, name);
        		// TODO Auto-generated constructor stub
        	}
        	
        	@Override
        	public boolean equals(Object obj) {
        		if(obj==this) return true;
        		if(obj instanceof Man) {
        			Man p = (Man) obj;
        			return this.age==p.age&&this.name.equals(p.name);
        		}
        		return false;
        	}
        }
        
        package com.shc.java3;
        
        public class Person {
        	int age;
        	String name;
        	public Person(int age, String name) {
        		super();
        		this.age = age;
        		this.name = name;
        	}
        
        	@Override
        	public boolean equals(Object obj) {
        		if(obj==this) return true;
        		if(obj instanceof Person) {
        			Person p = (Person) obj;
        			return this.age==p.age&&this.name.equals(p.name);
        		}
        		return false;
        	}
        }
        
        
  • toString()使用

    • Object类中toString()的使用

        1. 当我们输出一个对象时,实际上就是调用当前对象的toString()
        1. Object类中toString()的定义
      • 		public String toString() {
        			return getClass().getName() + "@" + Integer.toHexString(hashCode());
            	}
        
        1. 像String、Date、File、包装类等都重写了Object类中的toString()方法,使得对象调用toString时,返实体内容
        1. 自定义类也可以重写toString()方法,当调用此方法时,返回对象的"实体内容"
    • 特殊情况syso(sth)就是syso(sth.toString())吗? 不是

      • 	@Test
        	public void test3() {
        		String s = "abc";
        		s=  null;
        		System.out.println(s);//null;
        		System.out.println(s.toString());//NullPointerException
        //		java.lang.NullPointerException: Cannot invoke "String.toString()" because "s" is null
        	}
        
      • 原因:println()会判断是否为null

         public void println(String x) {
              if (getClass() == PrintStream.class) {
                  writeln(String.valueOf(x));
              } else {
                  synchronized (this) {
                      print(x);
                      newLine();
                  }
              }
          }
      
          public static String valueOf(Object obj) {
              return (obj == null) ? "null" : obj.toString();
          }
      
    • 如何重写toString() 简单

关于数组也可以看作是一种继承于Object的特殊的类

证明

public class ReviewTest {
	@Test
	public void test1() {
		int[] arr1 = new int[] {1,2,3};
		print(arr1);
		//没有报错 可以执行 证明可以把数组看成一个特殊的类
		//[I@1f0f1111
		System.out.println(arr1.getClass());;
		System.out.println(arr1.getClass().getSuperclass());
	}
	
	public void print(Object obj) {
		System.out.println(obj);
	}
}
[I@7ee955a8
class [I
class java.lang.Object

s单元测试方法

* Java中的Unit单元测试
 * 
 * 步骤
 * 1. 选中当前工程- 右键选择:build path - add libraries -Junit 4
 * 2. 创建Java类,进行单元测试
 * 此时的Java类要求:(1)此类是public的。(2)此类提供公共的无参构造器
 * 3. 此类中声明单元测试方法:
 * 此时的单元测试方法,方法的权限是public,没有返回值,没有形参
 * 4. 此单元测试方法上需要生命注解:@Test,并在单元测试类中导入:import org.junit.Test
 * 5. 声明好单元测试方法以后,就可以在方法内测试相关的代码
 * 6. 写完代码以后,左键双击单元测试方法名,右键run as - JUnit Test
 * 
 * 说明:
 * 1. 如果执行结果没有任何异常,绿条
 * 2. 如果执行结果出现异常,红条
 * 
 * 开发中不用新建类,可以直接在要测试的方法上+@Test,再导入JUnit4 library

包装类

  • 简易版:

    • 基本数据类型 <---> 包装类: JDK 5.0 新特性:自动装箱,自动拆箱
    • 基本数据类型、包装类 ---->String :调用String重载的valueOf(Xxx xx)
    • String ---> 基本数据类类型、包装类:调用包装类的parseXxx(String s)
    • 注意:转换时可能会爆NumberFormatException
  • 应用场景举例

    • Vector类中关于添加元素,只定义了形参为Object类型的方法:

      v.addElement(Object obj); //基本数据类型 --> 包装类 ---> 使用多态
      

每天一考

    1. 什么是多态性?
    • 父类的引用指向子类的对象

    Person p = new Man();
    p.eat();
    
    • 父类引用变量掌管子类对象时 调用子父类同时有的方法时调的是子类方法。

    • 调用方法,编译时看左边,运行时看右边

    • 一个类只能有一个直接父类
    • 一个父类可有多个子类
    • (多个)子类可以获取直接父类的的父类的结构
    • 子类可以获取父类中private权限的属性或方法
    • 但是子类不能直接访问,因为封装性。(子类看不到那个属性,方法)
    1. 方法的重写(override/overwrite)
    • 方法名,形参列表相同
    • 权限修饰符
    • 返回值
    • 抛出的异常
    1. super,this调用构造器
    • super(形参列表):本类重载其他的构造器
    • this(形参列表):调用父类中的指定构造器
    • 一个类的实例化过程一定会逐步调用他的所有父类构造器,即会加载它的父类以及它父类的父类知道Object。正因为加载了所有父类的结构,所以才有了继承性,堆中才会有父类的属性和方法。
  • 标准题

instanceof

  • instanceof使用:a instanceof A:判断对象a是否是类A的实例,如果是,则返回true;如果不是,返回false。
  • 使用情景:为了避免向下转型时出现ClassCastException的异常,我们在向下转型之前,先进行instanceof的判断,一旦返回true,就进行向下转型。如果返回false,则不进行向下转型。
  • 如果a instanceof A则返回true,则a instanceof B也返回true,其中,类B是类A的父类。
  • 如果a的A的子类的对象,那么也是返回true
  • 注意 对象 instanceof 类

向下转型

package com.shc.exer6;
/*
 * 多态性
 * 1.理解多态性:可以理解为一个事物的多种形态
 * 2.何为多态性? 父类的引用指向子类的对象
 * 3.多态的使用:虚拟方法的调用
 * 	有了对象的多态性以后,我们在编译时,只能调用父类中声明的方法,但是在运行期间,我们实际执行的是子类重写父类的方法
 * 总结:编译看左,运行看右
 * 4.多态性的使用前提(1)类的继承关系 (2)子类方法的重写 意义:省的设计多个重复函数
 * 5.对象多态性:只适用于方法,不适用于属性(属性的编译和运行都看左边)
 */
public class PersonTest {
	public static void main(String[] args) {
		Person person = new Person();
		person.eat();
		
		Man man = new Man();
		man.eat();
		man.walk();
		man.earnMoney();
		System.out.println("**********");
		//对象的多态性:父类的引用指向子 类的对象
		Person p2 = new Man();
		//多态性的使用:当调用子父类同名参数的方法时,实际执行的是子类重写父类的方法---虚拟方法的调用
		p2.eat();
		p2.walk();
//		p2.earnMoney(); //不能通过父类引用调用子类特有的方法
		
		System.out.println("********************");
		p2.name = "shc";
//		p2.earnMoney();
//		p2.isSmoking = false;
		//右链对象的多态性以后,**内存中实际上是加载了子类特有的属性和方法的**,
		//但是由于变量声明为父类类型,导致编译时,只能调用父类中声明的属性和方法,子类特有的属性和方法不能调用
		//如何才能调用子类特有的属性和方法?
		//向下转型:
		Man m1 = (Man)p2;
		m1.earnMoney();
		m1.isSmoking = true;
		
		//练习:
		//问题一:编译通过,运行时不通过
//		Person p3 = new Woman();
//		Man m3 = (Man)p3;
		
		//问题二:编译通过,运行时不通过
//		Person p4 = new Person();
//		Man  m4 = (Man)p4;
		
		//问题三:编译通,运行时也通过
//		Object obj = new Woman();
//		Person p = (Person)obj;
		
		//问题四:编译不通过 不相关的的两个类不能转型
//		Man m5 = new Woman();
	}
}

Object 类

package com.shc.java1;

/*
 * java.lang.Object类
 * 1. Object类是所有Java类的根父类
 * 2. 如果在类的声明未使用extends指明父类,则默认父类为java.lang.Object类
 * 3. Object类中的功能(属性、方法)就具有通用性
 * 属性:无
 * 方法:equals()/toString()/getClass()/hashCode*(/clone()/finalize()/wait、notify()、notifyAll()
 * 4. Object类中只声明了一个空参构造器
 * 
 */
public class ObjectTest {
	public static void main(String[] args) {
		Order order = new Order();
		System.out.println(order.getClass()); //class com.shc.java1.Order

		System.out.println(order.getClass().getSuperclass()); //class java.lang.Object
	}
}
class Order{
	
}

finalize()

  • 垃圾回收机制关键点
    垃圾回收机制只回收JVM堆内存里的对象空间。
    对其他物理连接,比如数据库连接、输入流输出流、Socket连接无能为力
    现在的JVM有多种垃圾回收实现算法,表现各异。
    垃圾回收发生具有不可预知性,程序无法精确控制垃圾回收机制执行。
    可以将对象的引用变量设置为null,暗示垃圾回收机制可以回收该对象。
    程序员可以通过System.gc()或者Runtime.getRuntime().gc()来通知系统进行垃圾回收,会有一些效果,但是系统是否进行垃圾回收依然不确定。
    垃圾回收机制回收任何对象之前,总会先调用它的finalize方法(如果覆盖该方法,让一个新的引用变量重新引用该对象,则会重新激活对象)。
    永远不要主动调用某个对象的finalize方法,应该交给垃圾回收机制调用。

public class FinalizeTest {
	public static void main(String[] args) {
		Personx p = new Personx("Peter", 12);
		System.out.println(p);
		p = null;//此时对象实体就是垃圾对象,等待被回收。但时间不确定。
		System.gc();//强制性释放空间 通知JVM来回收 但不一定立刻来
	}
}

class Personx{
	private String name;
	private int age;

	public Personx(String name, int age) {
		super();
		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;
	}
	//子类重写此方法,可在释放对象前进行某些操作
	@Override
	protected void finalize() throws Throwable {
		System.out.println("对象被释放--->" + this);
	}
	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + "]";
	}
	
}
//Person [name=Peter, age=12]
//对象被释放--->Person [name=Peter, age=12]

Clone()

package com.shc.java1;
//Object类的clone()的使用
public class CloneTest {
	public static void main(String[] args) {
		Animal a1 = new Animal("花花");
		try {
			Animal a2 = (Animal) a1.clone();
			System.out.println("原始对象:" + a1);
			a2.setName("毛毛");
			System.out.println("clone之后的对象:" + a2);
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
	}
}

class Animal implements Cloneable{
	private String name;

	public Animal() {
		super();
	}
	public Animal(String name) {
		super();
		this.name = name;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	@Override
	public String toString() {
		return "Animal [name=" + name + "]";
	}
	@Override
	protected Object clone() throws CloneNotSupportedException {
		// TODO Auto-generated method stub
		return super.clone();
	}
}
原始对象:Animal [name=花花]
clone之后的对象:Animal [name=毛毛]

Equals()

package com.shc.java1;

import java.util.Date;
/*
 * == 与 equals的区别
 * 一、==
 * ==:运算符
 * 1. 可以使用在基本数据类型变量和引用数据类型变量中
 * 2. 如果比较的是基本数据类信号变量,比较两个变量保存的数据是否相等(不一定要类型相同,存在自动类型提升)
 * 	 如果比较的是引用类型,比较两个对象的地址值是否相同,即两个引用是否指向同一个对象实体
 * 3. 使用“==”进行比较时,符号两边的数据类型必须兼容(可自动转换的基本数据类型除外)
 *二、equals()方法的使用
 * 1. 是一个方法,而非运算符
 * 2. 只能适用于引用数据类型
 * 3. Object类中equals()定义
 * 		public boolean equals(Object obj){
 * 			return (this==obj);
 * 		}
 * 4. 如String、Date、File、包装类等都重写了Object类中的equals()方法。
 * 重写以后,比较的不是两个引用地址是否相同,而是比较两个对象的实体内容是否相同。
 * 5. 通常情况下,我们自定义的类如果使用equals()的话,也通常是比较两个对象的实体内容是否相同,那么,我们就要对Object类中的equals()进行重写
 * 
 * 重写equals时,基本数据类型用==,引用数据类型用equals(要用重写过Object中的equals的equals
 * 开发中 通常直接alt+shift+s 直接自动生成
 */
public class EqualTest {
	public static void main(String[] args) {
		int i=10;
		int j=10;
		double d = 10.0;
		System.out.println(i==j);
		System.out.println(i==d);
		
		boolean b = true;
//		System.out.println(i==c);编译error
		
		char c1 = 'A';
		char c2 = 65;
		char c3 = 10;
		System.out.println(c1==c2);//true
		System.out.println(c3==i);//true
		//引用类型
		Person person = new Person("shc",12);
		Person person2 = new Person("shc",12);
		System.out.println("***************");
		System.out.println(person==person2);//false
		System.out.println(person.equals(person2));//Person类重写Object类中的equals之后是true 未重写之前是false
		
		System.out.println("***************");
		String str1 = new String("shc");
		String str2 = new String("shc");
		System.out.println(str1.equals(str2));//true
		
		Date date = new Date(1231L);
		Date date2 = new Date(1231L);
		System.out.println(date.equals(date2));//true
	}
}

package com.shc.java1;

public class Person {
	private String name;
	private int age;
	
	public Person() {}
	
	public Person(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	//alt+shift+s自动生成
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Person other = (Person) obj;
		if (age != other.age)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}
//	手动实现equals
//	@Override
//	public boolean equals(Object obj) {
//		if(this==obj) return true; //==:对引用对象来说是比较地址值
//		if(obj instanceof Person) { //如果可以向下转型的话
//			Person p = (Person)obj; //为了调用真实对象的属性
//			return this.age==p.age&&this.name.equals(p.name);
//			//age:基本数据类型比较 ==比较内容
//			//String:String内容比较 equals()方法
	//错误写法:对引用对象来说==比较的是地址 "AA"=="AA" 为true ; new String("AA")==AA 为false
//	return this.age==p.age&&this.name==(p.name);
	
//		}
//		return false;
//	}	
}
true
true
true
true
***************
false
true
***************
true
true

toString()

package com.shc.java1;

import java.util.Date;
/*
 * Object类中toString()的使用
 * 1. 当我们输出一个对象时,实际上就是调用当前对象的toString()
 * 2. Object类中toString()的定义
 *      public String toString() {
        	return getClass().getName() + "@" + Integer.toHexString(hashCode());
    	}
 * 3. 像String、Date、File、包装类等都重写了Object类中的toString()方法,使得对象调用toString时,返实体内容
 * 4. 自定义类也可以重写toString()方法,当调用此方法时,返回对象的"实体内容"
 */
public class ToStringTest {
	public static void main(String[] args) {
		Person p = new Person();
		System.out.println(p);//实际上也是调用了toString(看源码)com.shc.java1.Person@3c1
		System.out.println(p.toString());//com.shc.java1.Person@3c1
		
		String str = new String("MM");//MM
		System.out.println(str);
		
		Date date = new Date(64664566L);
		System.out.println(date);//Fri Jan 02 01:57:44 CST 1970
	}
}
println()
     public void println(String x) {
        if (getClass() == PrintStream.class) {
            writeln(String.valueOf(x));
        } else {
            synchronized (this) {
                print(x);
                newLine();
            }
        }
    }

valueof()
    public static String valueOf(Object obj) {
        return (obj == null) ? "null" : obj.toString();
    }

toString()
	public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode()); //hashCode()通过hashcode的值计算地址值(在JVM中的)(虚拟的)(不是我们真实操作系统的) Integer.toHexString 转化为16进制
    }

package com.shc.java;

import org.junit.Test;

public class JUnitTest2 {
	@Test
	public void TesttoString() {
		char[] arr = new char[] { 'a', 'b', 'c' };
		System.out.println(arr);//abc
		int[] arr1 = new int[] { 1, 2, 3 };
		System.out.println(arr1);//[I@7ee955a8
		double[] arr2 = new double[] { 1.1, 2.2, 3.3 };
		System.out.println(arr2);//[D@1677d1
	}
}

包装类

差一个练习 建议做 尚硅谷P310

package com.shc.java3;

import org.junit.Test;

/*
 * 包装类的使用:
 * 1. Java提供了8种基本数据类型对应的包装类,使得基本数据类型的变量具有类的特征
 * 2. 掌握:基本数据类型、包装类、String三者之间的相互转换
 */
public class WrapperTest {
	boolean isMale; //isMale是一个基本数据类型的变量 默认值false
	Boolean isFeMale;  //isFemale是一个包装类的对象 默认值null
	
	//String类型 ---->基本数据类型.包装类
	//调用包装类的parseXxx()
	@Test
	public void test5() {
		String str1 = "123";
		//只有两个类之间有子父类关系才可以强转
//		int num1 = (int)str1; error
//		Integer in1 = (Integer)str1;error Can not cast String to Integer 
		
		//正确写法: paresXxxx()
		//除了boolean之外 可能会爆NumberFormatException
		int num2 = Integer.parseInt(str1);
		System.out.println(num2+1);
		
		String str = "true1";
		boolean st = Boolean.parseBoolean(str);
		System.out.println(st);//false 不是标准的true就认为false
	}
	
	//3.基本数据类型,包装类-->String类型,调用Strin重载的valueOf(xxx)
	@Test
	public void test4() {
		System.out.println("******************");
		int num1 = 10;
		//方式一:连接运算
		String str1 = num1+"";
		//方式二:调用String的valueOf(Xxx xxx);
		float f1 = 12.3f;
		String str2 = String.valueOf(f1);
		
		Double d1 = new Double(12.4);
		String str3 =  String.valueOf(d1);
		System.out.println(str3);
		System.out.println(str2);
		System.out.println("******************");
	}
	
	
	//1. 基本数据类型--->包装类 常用!!
	@Test
	public void test1() {
		int a = 10;
//		System.out.println(a.toString()); //Cannot invoke toString() on the primitive type int
		Integer a1 = new Integer(a);
		System.out.println(a1.toString());//10
		
		Integer b = new Integer("123");
		System.out.println(b.toString());//123
		
//		Integer c = new Integer("223abc");Exception
		Float f1 = new Float(12.3f);
		Float f2 = new Float(12.3);
		Float f3 = new Float("12.3");
		System.out.println(f1); //12.3
		System.out.println(f2); //12.3
		System.out.println(f3);//12.3 可以省略toString()
		
		Boolean c = new Boolean(true);
		Boolean c1 = new Boolean("true123");//无Exception
		System.out.println(c);//true
		System.out.println(c1);//false
//		public static boolean parseBoolean(String s) {
//	        return "true".equalsIgnoreCase(s);
//	    }忽略大小写,与true一样即为true
		System.out.println(isMale); //false
		System.out.println(isFeMale); //null	
	}
	
	
	//2. 包装类--->基本数据类型:调用了包装类的xxxValue() 常用!!
	//转化目的:做加减乘除运算 类不能做加减乘除运算 要转化成基本数据类型
	//单元测试:鼠标选中该方法 在alt+shift+x,t
	@Test
	public void test2() {
		Integer in1 = new Integer(12);
		int i1 = in1.intValue();
		System.out.println(i1+1);
		
		Float f1 = new Float(12.3);
		float f2 = f1.floatValue();
		System.out.println(f2);
		
	}
	
	/*
	 * JDK 5.0 新特性:自动装箱与自动拆箱
	 */
	//应用举例
	@Test
	public void test3() {
		int num1 = 10;
		//基本数据类型 --> 包装类的对象
		method(new Integer(num1));//转化成类 再利用多态将其放入
		method(num1); //也可以
		
		//自动装箱:
		int num2 = 10;
		Integer in1 = num2;//一个int型的变量居然可以直接赋给一个类!? 这就是自动装箱
		boolean b1 = true;
		Boolean b2 = b1;
		System.out.println(in1.toString());//10
		
		//自动拆箱:
		int num3 = in1;
		System.out.println(num3+3);//13
	}
	
	public void method(Object obj) {
		
	}
}

//integer--value 就是我们的int值 用integer包起来了
package com.shc.java3;

import org.junit.Test;

//简单理解包装类:把核心的基本数据类型变量封装一下,作为这个类的一个属性出现,再加一些方法
public class InterviewTest {
	@Test
	public void test1() {
		//三元运算符
		//编译时要保证":"两端是同一类型的数据 所以Integer自动类型提升为Double
		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
	}
	
	//println(): 对于数组 char型数组 输出内容 其他的如int型输出地址值
	//Integer内部定义了IntegerCache结构,IntegerCache中定义了Integer[]
	//保存了-128~127范围的整数,如果我们使用自动装箱的方式,给Integer赋值范围在-128~127之内时,可以直接使用数组中的元素,不用再去new
	//目的:提高效率
	@Test
	public void method1() {
		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;//相当于new了一个对象
		Integer y = 128;
		System.out.println(x == y);// false
	}
}
/*
 * private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer[] cache;
        static Integer[] archivedCache;

        static {
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                try {
                    h = Math.max(parseInt(integerCacheHighPropValue), 127);
                    // Maximum array size is Integer.MAX_VALUE
                    h = Math.min(h, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                    // If the property cannot be parsed into an int, ignore it.
                }
            }
            high = h;

            // Load IntegerCache.archivedCache from archive, if possible
            VM.initializeFromArchive(IntegerCache.class);
            int size = (high - low) + 1;

            // Use the archived cache if it exists and is large enough
            if (archivedCache == null || size > archivedCache.length) {
                Integer[] c = new Integer[size];
                int j = low;
                for(int i = 0; i < c.length; i++) {
                    c[i] = new Integer(j++);
                }
                archivedCache = c;
            }
            cache = archivedCache;
            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }

        private IntegerCache() {}
    }
 */

每天一考

    1. 如何实现向下转型?需要注意什么问题?
    Person p = new Man();
    //使用强转符
     Man m = (Man) p;   
    
    • 可能ClassCastException异常
    • 解决方法:instanceof
    • 使用instanceof在进行向下转型前判断
    • 向下转型用的没有多态多,但是有必须用的时候.
    if(p instanceof Man){
    }
    p是变量 M是类型!!
    
    1. "==" 与 equals() 有何区别
    1. 写出8种基本数据类型及其对应的包装类
    • 基本数据类型 包装类
      byte Byte
      short Short
      int Integer
      long Long
      float Float
      double Double
      boolean Boolean
      char Character
    • Byte, Short, Integer, Long, Float, Double是抽象类Number的子类

    1. 基本数据类型、包装类与String三者之间如何转换
    • 基本数据类型与包装类:

      • 自动装箱,自动拆箱

      • Integer i = 10;

    • 基本数据类型和包装类如何转化成String?

      • 基本数据类型、包装类 ----> String valueOf(Xxx xx)
      • String --- >基本数据类型、包装类 :parseXxx(String s)

``

posted on 2021-05-06 18:44  咕噜辰  阅读(66)  评论(0)    收藏  举报