Java基础二:封装,继承,多态,Object类

封装和隐藏

四种访问权限修饰符
修饰符类内部同一个包不同包的子类同一个工程
privateYes
(缺省)YesYes
protectedYesYesYes
publicYesYesYesYes

对class的权限修饰只可以用public和缺省


构造器

  • 如果没有显示自定义构造器,系统会提供默认空参构造器
  • 一旦显示自定义构造器,系统不会再提供默认空参构造器
  • 父类的构造器不会被子类继承
  • 构造器重载,参数列表必须不同

拓展:JavaBean

  • JavaBean是一种Java语言写成的可重用组件
  • 所谓javaBean,是指符合如下标准的Java类:
    (1)类是公共的
    (2)有一个无参的公共构造器
    (3)有属性,且有对应的get、set方法
  • 用户可以认为JavaBean提供了一种随时随地的复制和粘贴的功能,而不用关心任何改变

拓展:UML类图
在这里插入图片描述


this关键字
  • 可以在类的构造器中使用"this(形参列表)"的方式,调用本类中重载的其他的构造器
  • 构造器中不能通过"this(形参列表)"的方式调用自身构造器
  • 如果一个类中声明了n个构造器,则最多有 n - 1个构造器中使用了"this(形参列表)"
  • "this(形参列表)"必须声明在类的构造器的首行
  • 在类的一个构造器中,最多只能声明一个"this(形参列表)"
super关键字
  • 子类中所有的构造器默认都会访问父类中空参数的构造器
  • 当父类中没有空参数的构造器时,子类的构造器必须通过this(参数列表)或者super(参数列表)语句指定调用本类或者父类中相应的构造器。同时,只能”二选一”,且必须放在构造器的首行
No.区别点thissuper
1访问属性访问本类中的属性,如果本类没 有此属性则从父类中继续查找直接访问父类中的属性
2调用方法访问本类中的方法,如果本类没有此方法则从父类中继续查找直接访问父类中的方法
3调用构造器调用本类构造器,必须放在构造器的首行调用父类构造器,必须 放在子类构造器的首行

关于this和super构造器只能二选一并且要放在第一行:
首先,必须在构造器的第一行放置super或者this构造器,否则编译器会自动地放一个空参数的super构造器的,其他的构造器也可以调用super或者this,调用成一个递归构造链,最后的结果是父类的构造器(可能有多级父类构造器)始终在子类的构造器之前执行,递归的调用父类构造器。无法执行当前的类的构造器。也就不能实例化任何对象,这个类就成为一个无为类。 从另外一面说,子类是从父类继承而来,继承了父类的属性和方法,如果在子类中先不完成父类的成员的初始化,则子类无法使用,应为在java中不允许调用没初始化的成员。在构造器中是顺序执行的,也就是说必须在第一行进行父类的初始化。而super能直接完成这个功能。This()通过调用本类中的其他构造器也能完成这个功能。 因此,this()或者super()必须放在第一行。
只能二选一,因为第一行只有一个。另外,如果调用了this,那this里面必然又调用了super或this,这样追溯上去,总有一个构造器是调用了super,这样一路继承下来。所以没必要再调super了。


多态

  • 体现:父类的引用指向子类的对象,如,Student是继承自Person的:
Person p = new Student();
Object o = new Person();
o = new Student();
  • Java引用变量有两个类型:编译时类型和运行时类型。编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。简单来说:编译时看左边,运行时看右边
    • 看左边:看的是父类的引用(父类中不具备子类特有的方法)
    • 看右边:看的是子类的对象(实际运行的是子类重写父类的方法)
  • 编译时类型和运行时类型不一致,就出现了对象的多态性
  • 子类的对象可以替代父类的对象使用
    • 一个变量只能有一种确定的数据类型
    • 一个引用类型变量可能指向多种不同类型的对象
  • 子类可看作是特殊的父类,所以父类类型的引用可以指向子类的对象,称为向上转型
  • 一个引用类型变量如果声明为父类的类型,但实际引用的是子类对象,那么该变量就不能再访问子类中特有的属性和方法。因为属性是在编译时确定的,编译时为父类类型,所以没有子类特有的成员变量。 如下:
Student s = new Student();
s.school = "XU";	//合法,Student类有school成员变量
Person p = new Student();
p.school = "XU";	//非法,Person类没有school成员变量
p.setName("XU");		//合法,Person类和Student类都有setName方法,但调用的是Student类的setName方法

如果是这么一种情况:
Personset(String name)方法,Studentset(String name, int number)方法,那么以下情况:

Person p = new Student();
p.setName("XU");	//合法,调用的是Person类的setName。如果子类重写了此方法,则调用的是子类的setName方法
p.setName("XU", 10);	//非法,子类特有的方法,调用不了
Student s = new Student();
s.setName("XU");	//合法,调用的是Person类的setName。如果自己重写了此方法,则调用自己的
s.setName("XU", 10);	//合法,调用自身方法

编译时pPerson类型,而方法调用是在运行时确定的,所以调用的是Student类的setName方法,这叫做动态绑定


再议重载与重写
  • 重载,是指允许存在多个同名方法,而这些方法的参数不同。编译器根据方法不同的参数列表,对同名方法的名称做修饰。对于编译器而言,这些同名方法构成了不同的方法。它们的调用地址在编译期就绑定了。Java中子类可以重载父类同名不同参的方法。对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法,这称为静态绑定
  • 重写,指有多个同名方法,并且参数列表相同,但方法体不同。*(对编译器而言它们是相同的方法?)*Java中子类可以重写父类同名同参的方法。只有等到方法调用的那一刻,解释运行器才会确定所要调用的具体方法,这称为动态绑定
  • 方法的重写是多态的前提之一

多态小结
  • 方法:
    • 编译时:要查看引用变量所声明的类中是否有所调用的方法
    • 运行时:调用实际new的对象所属的类中的重写方法
  • 属性:不具备多态性,只看引用变量所声明的类,即声明谁,引用谁。
  • 在处理多态时,可以从父类向子类推进,用instanceof先检查类型,然后再进行相应类的操作,如下:
if(p instanceof Person)
	//进行Person类方法
if(p instanceof Student)
	//进行Student类方法
	

对象类型转换

对象类型转换是多态的一个体现

  • 基本数据类型的转换
    • 自动类型转换,如double d = 12.0f
    • 强制类型转换,如int a = (int)1200L
  • 对Java对象的强制类型转换:
    • 从子类到父类的类型转换可以自动进行
    • 从父类到子类的类型转换必须通过强制类型转换实现
    • 无继承关系的引用类型之间的转换是非法的
    • 在强制转型之前可以使用instanceof测试一个对象的类型

练习:多态是编译时行为还是运行时行为?如何证明?

//运行时行为

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();	//运行时才能知道具体创建对象
	}
}

下面记录一些自己实验出来的一些小结论:

//1.静态方法无法重写
class Person1{
    static String name = "Person";

    public static String getName(){
        System.out.println(name);
        return name;
    }
}

class Student1 extends Person1{
    static String name = "Student";

    public static String getName(){//此处的重写是无效的,即使创建Person对象指向Student,调用getName,输出和返回的依然是Person
        System.out.println(name);
        return name;
    }
}
public class Test {
    public static void main(String[] args) {
        Base base = new Sub();
        base.add(1, 2, 3);//方法被重写,输出的sub_1
        //最下面的add(int a, int b, int c)不会被调用,因为参数列表变了,它是重载不是重写,不符合多态要求

		Sub s = (Sub)base;
//		s.add(1, 2, 3);//如果没有最下面重载的add(int a, int b, int c)方法,这个语句会报错,
        // 因为此时是Sub对象,参数形式不符合Sub的add(int a, int[] arr)要求
        //作为重载方法,add(1, 2, 3)的参数形式相比于add(int a, int... arr)
        //更符合add(int a, int b, int c)。优先调用确定个数参数方法,所以调用add(int a, int b, int c)
        //所以这里输出的是sub_2
    }
}

class Base {
    public void add(int a, int... arr) {
        System.out.println("base");
    }
}

class Sub extends Base {

    public void add(int a, int[] arr) {//编译器认为int...和int[]是一样的形参(虽然实际上不一样),所以这也算是方法的重写
        System.out.println("sub_1");
    }

//	public void add(int a, int b, int c) {//这个方法不是重写,是重载
//		System.out.println("sub_2");
//	}


Object类

主要结构
No.方法名称类型描述
1public Object()构造构造器
2public boolean equals(Object obj)普通对象比较
3public int hashCode()普通取得Hash
4public String toString()普通转换为字符串

==与equals方法
  • ==
    • 基本数据类型比较值:只要两个变量的值相等,即为true
    • 引用数据类型比较引用(比较地址值,是否指向同一个对象):只要指向同一个对象,即为true
    • ==进行比较时,两边的数据类型必须兼容(可自动转换的基本数据类型除外,如1 == 1.0)
  • equals()
    • 只能比较引用类型,比较是否指向同一个对象
    • FileStringDate及包装类来说,它们重写过equals方法,比较的是类型及内容,而不考虑是否是同一个对象
    • 自定义类可以重写equals方法,一般都重写,用于比较两个对象的内容是否相等

==和equals的区别
  • ==既可以比较基本类型也可以比较引用类型,对于基本类型就是比较值,对于引用类型就是比较内存地址
  • equals是属于java.lang.Object类里的方法,如果该方法没有被重写过,默认功能和==一样;一般使用都是重写过的,比如String等类型,比较的是内容
  • 具体要看自定义类里有没有重写equals方法来判断。通常情况下,重写equals都会比较类中的相应内容是否相等

toString方法
  • Object中,toString返回值是String类型,返回类名和它的引用地址
  • 在进行String与其他类型数据连接操作时,自动调用toString方法
  • String和一些常用类已经重写了toString方法,直接输出内容
  • 自定义类可以重写toString方法,一般都重写,把需要的内容转换为字符串输出

包装类

  • 针对八种基本数据类型定义相应的引用类型—包装类,使它们有了类的特点,就可以调用类中的方法,Java才是真正的面向对象
基本数据类型包装类
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
booleanBoolean
charCharacter
包装类的使用
  • 字符串转换为基本数据类型:
    • int i = new Integer("12");
    • Float f = Float.parseFloat("12.1");
  • 基本数据类型转换为字符串
    • String fStr = String.valueOf(2.34f);
    • String intStr = 5 + ""

这里主要记一下parseXxxvalueOf两种方法
在这里插入图片描述


思考题:

1.考虑下面两段程序的输出:

Object o1 = true ? new Integer(1) : new Double(2.0); 
System.out.println(o1);
Object o2; 
if (true)
	o2 = new Integer(1); 
else
	o2 = new Double(2.0); 
System.out.println(o2);

第一段程序:输出:1.0。因为这个三元运算符后面两个运算对象需要类型统一,所以Integer类型自动提升为Double类型。Object o1 = new Integer(1),多态,调用的是IntegertoString方法,所以输出内容

第二段程序:输出1,不存在类型转换

2.思考下面程序的输出

public void method1() {
	Integer i = new Integer(1); 
	Integer j = new Integer(1); 
	System.out.println(i == j);
	
	Integer m = 1;
	Integer n = 1; 
	System.out.println(m == n);
	
	Integer x = 128;
	Integer y = 128; 
	System.out.println(x == y);
}

包含的知识点:Integer内部提前定义了IntegerCache结构,其中有一个Integer[],保存了-128~127范围的整数。如果使用自动装箱的方式给Integer赋值这个范围的数,可以直接使用数组中的元素,不需要再使用new方法创建。出了这个范围的数,依然需要使用new方法创建
所以三个输出分别是:falsetruefalse

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值