学习JavaEE的日子 day16 抽象类,接口,多态,对象转型,内部类

Day16

1.抽象类及抽象方法的使用

*我们把没有方法体的方法称为抽象方法。Java语法规定,包含抽象方法的类就是抽象类*

*注意:抽象类不一定有抽象方法,但是有抽象方法的类必须定义成抽象类。*

需求:编写人类为父类,编写两个子类(中国人、日本人)

抽象方法:没有代码块,使用abstract修饰的方法,交给非抽象的子类去实现

注意:

抽象方法必须在抽象类中

继承抽象类的子类****必须重写父类所有的抽象方法****。否则,该子类也必须声明为抽象类。

//抽象类:使用abstract修饰       
public abstract class Person {//父类

	private String name;
	private char sex;
	private int age;
	
	//无参构造,有参构造,get、set方法,省略
	
	//抽象方法:没有代码块,使用abstract修饰的方法,交给非抽象的子类去实现
	//注意:抽象方法必须在抽象类中
	public abstract void eat();
	
	public void sleep(){
		System.out.println(this.name + "睡觉觉");
	}
}
public abstract class Chinese extends Person{

	private String id;
	
	public Chinese() {
	}

	public Chinese(String name, char sex, int age, String id) {
		super(name, sex, age);
		this.id = id;
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}
	 
	public abstract void hobbies();
	
}
public class SiChuan extends Chinese{
	
	public SiChuan() {
	}
	
	public SiChuan(String name,char sex,int age,String id){
		super(name, sex, age, id);
	}

	@Override
	public void hobbies() {
		System.out.println(super.getName() + "喜欢打麻将、炸金花");
	}

	@Override
	public void eat() {
		System.out.println(super.getName() + "吃火锅、串串香");
	}

}
public class Japanese extends Person{

	private String yearNum;
	
	public Japanese() {
	}
	
	public Japanese(String name, char sex, int age, String yearNum) {
		super(name, sex, age);
		this.yearNum = yearNum;
	}

	public String getYearNum() {
		return yearNum;
	}

	public void setYearNum(String yearNum) {
		this.yearNum = yearNum;
	}

	@Override
	public void eat() {
		System.out.println(super.getName() + "吃马赛克");
	}

}
//测试类
public class Test01 {
	public static void main(String[] args) {
		
		Japanese j = new Japanese("波多野结衣", '女', 18, "令和");
		j.eat();
		j.sleep();
		
		System.out.println("-----------------------");
		
		SiChuan sc = new SiChuan("小彭", '男', 21, "1234567890");
		sc.eat();
		sc.hobbies();
		sc.sleep();
		
		System.out.println("-----------------------");
		
		GuangDong gd = new GuangDong("李嘉诚", '男', 97, "0987654321");
		gd.eat();
		gd.hobbies();
		gd.sleep();
	}
}

1.2抽象类的细节

1.抽象类****不能创建对象****,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象。

理解:假设创建了抽象类的对象,调用抽象的方法,而抽象方法没有具体的方法体,没有意义。

2.抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的。

理解:子类的构造方法中,有默认的super(),需要访问父类构造方法。

3.抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。

理解:未包含抽象方法的抽象类,目的就是不想让调用者创建该类对象,通常用于某些特殊的类结构设计。

4.抽象类的子类,必须重写抽象父类中****所有的****抽象方法,否则子类也必须定义成抽象类,编译无法通过而报错。

理解:假设不重写所有抽象方法,则类中可能包含抽象方法。那么创建对象后,调用抽象的方法,没有意义。

5.抽象类存在的意义是为了被子类继承。

理解:抽象类中已经实现的是模板中确定的成员,抽象类不确定如何实现的定义成抽象方法,交给具体的子类去实现。

2.抽象类及抽象方法的深入

面试题

1.抽象类不能有构造方法?

抽象类可以有构造方法

2.抽象类中只能有抽象方法?
抽象类中可以有属性、构造方法、成员方法、静态方法、抽象方法

3.抽象类中不可以没有抽象方法?
可以,但是这样毫无意义
(因为某些原因将方法设计成抽象的,抽象方法必须在抽象类中,这是你才会使用到抽象类)

4.什么原因将方法设计成抽象的?
这个方法应该在该类中,但是该方法不太好实现,就将方法设计成抽象的,交给非抽象的子类去实现

5.如果父类是抽象类,则子类必须实现父类的抽象方法?
不一定,如果子类是抽象类,可以不实现父类的抽象方法

6.可以使用new关键字来创建抽象类对象?
不可以,创建的是匿名子类的对象

public class Test02 {
	public static void main(String[] args) {
		
		Person p = new Person("弗罗兹·甘地",'男',23) {//底层是创建匿名类内类,不是抽象类
			
			@Override
			public void eat() {
				System.out.println(super.getName() + "吃咖喱");
			}
		};
		p.eat();
		
	}
}

匿名类内类内存理解图1:

在这里插入图片描述

匿名类内类创建过程:

1.创建一个没有名字的匿名类(子类),继承Person类(父类),重写eat方法

2.创建匿名子类的对象

3.将匿名子类对象的内存地址赋值给父类的引用(多态)

3.接口的使用

理解:
1.接口是一个特殊的抽象类
2.JDK1.8之前,接口中只能有抽象方法及静态常量
3.JDK1.8开始,接口中允许使用抽象方法、静态常量、静态方法、默认方法

注意:
1.接口中的抽象方法默认添加public abstract(经验:一般把abstract去掉)
2.接口中的属性默认添加public static final (静态常量)
3.接口中的默认方法默认添加public

应用场景:接口相当于是制定规则(标准),再让实现类去实现

需求:设计学生管理系统项目的接口(列大纲)

分析:
学生管理系统管理一个一个的学生对象
管理 - 数据的操作:增、删、改、查

//实体类
public class Student {
	
	private String name;
	private char sex;
	private int age;
	private String classId;
	private String id;

	//无参构造,有参构造,get、set方法,省略

	@Override
	public String toString() {
		return "Student [name=" + name + ", sex=" + sex + ", age=" + age + ", classId=" + classId + ", id=" + id + "]";
	}
}
//学生管理系统的接口  (大纲)
public interface IStudentManagerSystem {
	
	//静态常量
	//默认使用public static final修饰
	int NAME = 1;
	int SEX = 2;
	int AGE = 3;
	int CLASS_ID = 4;
	int ID = 5;

	//抽象方法  (大纲)
	//默认使用public abstract修饰
	public void add(Student stu);//添加
	
	public void delete(String classId,String id);//删除
	
	public void update(String classId,String id,int type,Object val);//更新
	
	public Student getStu(String classId,String id);//查询
	
	//静态方法
	public static void method01(){
		System.out.println("IStudentManagerSystem接口中的静态方法");
	}
	
	//默认方法
	//默认使用public修饰
	default void method02(){
		System.out.println("IStudentManagerSystem接口中的默认方法");
	}
}
//学生管理系统的实现类
public class StudentManagerSystemImpl implements IStudentManagerSystem{

	@Override
	public void add(Student stu) {
	}

	@Override
	public void delete(String classId, String id) {
	}

	@Override
	public void update(String classId, String id, int type, Object val) {
	}

	@Override
	public Student getStu(String classId, String id) {
		return null;
	}

}
//测试类
public class Test01 {
	public static void main(String[] args) {
		
		StudentManagerSystemImpl sms = new StudentManagerSystemImpl();
		
		//调用实现类实现的方法
		sms.add(new Student());
		//调用默认方法
		sms.method02();
		//调用静态方法
		IStudentManagerSystem.method01();
	}
}

3.1定义格式

接口的声明:interface

接口名称:首字母大写,满足“驼峰模式”

interface 接口名称{

  // 抽象方法

}

注意:接口可以多继承,类只能单继承。

3.2接口的实现

实现接口的格式
//接口的实现:
class 类名 implements 接口1,接口2,接口3...{

}
3.2.1 类实现接口的要求和意义

1.必须重写实现的全部接口中所有抽象方法。

2.如果一个类实现了接口,但是没有重写完全部接口的全部抽象方法,这个类也必须定义成抽象类。

3.*意义:接口体现的是一种规范,接口对实现类是一种强制性的约束,要么全部完成接口申明的功能,要么自己也定义成抽象类。这正是一种强制性的规范。*

4.接口的深入

面试题

1.一个类可以实现多个接口?

可以

2.一个接口可以实现多个接口?

不可以

但是一个接口可以继承多个接口

3.接口里面的方法不一定都是抽象的?

JDK1.8之前,接口中只能有抽象方法和静态常量

JDK1.8开始,接口中除了有抽象方法和静态常量以外,还可以有静态方法和默认方法

4.接口解决了类的单继承问题?

是的,因为一个类可以实现多个接口

5.一个类是否可以继承一个类并同时实现多个接口?

可以

6.接口可以new对象?

不可以,创建的是匿名实现类的对象

类 - 接口的关系:

类 - 类:单继承(一个类只能继承另一个类,不能继承多个类)

类 - 接口:多实现(一个类可以实现多个接口)

接口 - 接口:多继承(一个接口可以继承多个接口)

public interface I1 {

	public void i1Method();
}
public interface I2 {

	public void i2Method();
}
public interface I3 extends I4,I5{

	public void i3Method();
}
public interface I4 {

	public void i4Method();
}
public interface I5 {

	public void i5Method();
}
public class B {

}
public class A extends B implements I1,I2,I3{//A 继承 B 实现I1,I2,I3接口

	@Override
	public void i2Method() {
	}

	@Override
	public void i1Method() {
	}

	@Override
	public void i4Method() {
	}

	@Override
	public void i5Method() {
	}

	@Override
	public void i3Method() {
	}

}

5.多态

多态性是指同一操作或方法可以在不同的对象上具有不同的行为。它允许我们通过使用基类或接口类型的引用变量来调用子类或实现类的方法。

理解:多种形态

分类:

类的多态:子类对象指向父类引用(父类引用中存储的是子类对象在堆中开辟的地址)

接口的多态:实现类对象指向接口的引用(接口的引用中存储的是实现类对象在堆中开辟的地址)

5.1类的多态

理解:子类对象指向父类引用;父类引用中存储的是子类对象在堆中开辟的内存地址

需求:使用代码描述出老师骑着自行车上班

分析:老师类、自行车类

需求升级:自行车 -> 小汽车

步骤:

1.创建Car类,编写open、close

2.改动原来的Teacher,编写start、stop

设计原则:前人给我们总结的经验,告诉我们不能做什么,如果做了会出现严重后果

设计模式:前人给我们总结的经验,告诉我们怎么做,我们就跟着他一步一步做就能实现功能

开闭原则 - OCP

O - Open - 在需求升级时,对于创建类是欢迎的(因为创建类对于原来代码的影响几乎为0)

C - Close - 在需求升级时,对于改动原有类是拒绝的(因为原有类之间的关系是趋于稳定状态,如果改动原有类,很有可能打破这种平衡,导致bug的出现)

P - Principle - 原则

优点:提高程序的维护性,在需求升级/迭代时,不违反OCP原则

缺点:不能调用子类独有的属性和方法

需求升级:自行车 -> 小汽车 -> 飞机

步骤:创建Plane类,继承Vehicles,重写open、close

//交通工具
public abstract class Vehicles {

	private int count;//座位数
	private String color;//颜色
	
	public Vehicles() {
	}
	
	public Vehicles(int count, String color) {
		this.count = count;
		this.color = color;
	}

	public int getCount() {
		return count;
	}

	public void setCount(int count) {
		this.count = count;
	}

	public String getColor() {
		return color;
	}

	public void setColor(String color) {
		this.color = color;
	}

	public abstract void open();
	
	public abstract void close();
}

public class Bick extends Vehicles{
	
	public Bick() {
	}
	
	public Bick(int count,String color){
		super(count, color);
	}
	
	public void open(){
		System.out.println("自行车:握好扶手,踩下脚踏板");
	}
	
	public void close(){
		System.out.println("自行车:捏手刹");
	}
}
public class Car extends Vehicles{
	
	public Car() {
	}
	
	public Car(int count,String color){
		super(count, color);
	}

	public void open(){
		System.out.println("小汽车:一键启动,挂D档,踩下油门");
	}
	
	public void close(){
		System.out.println("小汽车:踩刹车,挂P档,熄火");
	}
}
public class Plane extends Vehicles{

	@Override
	public void open() {
		System.out.println("飞机:踩油门");
	}

	@Override
	public void close() {
		System.out.println("飞机:达到P城,跳伞");
	}

}
public class Teacher {

	public void start(Vehicles v){
		v.open();
	}
	
	public void stop(Vehicles v){
		v.close();
	}
}
public class Test01 {
	public static void main(String[] args) {
		
		Teacher t = new Teacher();
		
		//类的多态:子类对象指向父类引用
		//理解:父类引用中存储的是子类对象在堆中开辟的内存地址
		Vehicles v = new Plane();
		
		t.start(v);
		System.out.println("欣赏沿途的风景");
		t.stop(v);
		 
	}
}

5.2接口的多态

理解:实现类对象指向接口的引用;接口的引用存储的是实现类对象在堆中开辟的地址

需求:使用代码描述出电脑连接外部设备

public interface IUSB {

	public void use();
}
public class Mouse implements IUSB{

	@Override
	public void use() {
		System.out.println("鼠标:左点点、右点点");
	}
}
public class KeyBoard implements IUSB{

	@Override
	public void use() {
		System.out.println("键盘:输入数据");
	}

}
//电脑类
public class Computer {

	//连接
	public void connection(IUSB usb){
		usb.use();
	}
}
public class Test01 {
	public static void main(String[] args) {
		
		Computer computer = new Computer();
		
		//接口的多态:实现类对象指向接口的引用
		//理解:接口的引用存储的是实现类对象在堆中开辟的地址
		IUSB usb = new KeyBoard();
		
		computer.connection(usb);
	}
}

5.3多态的形式

*多态是出现在继承或者实现关系中的*

多态体现的格式:

父类类型 变量名 = new 子类/实现类构造器;

变量名.方法名();

5.4多态的前提

*多态*: 是指同一行为,具有多个不同表现形式。

*前提【重点】*

1.有继承或者实现关系

2.方法的重写【意义体现:不重写,无意义】

3.父类引用指向子类对象【格式体现】

父类类型:指子类对象继承的父类类型,或者实现的父接口类型。

6.对象转型

6.1向上转型

自动转型 - 向上转型:子类类型 转 父类类型 (转 --> =)

父类类型 变量名 = new 子类类型();

注意:

1.向上转型就是多态!!!

2.向上转型后,可以调用父类属性

3.向上转型后,可以调用父类方法

4.向上转型后,不可以调用子类独有的属性,方法

5.向上转型后,可以调用子类重写父类的方法

public class Father {

	String fatherAttr = "父类属性";
	
	public void fatherMethod(){
		System.out.println("父类方法");
	}
	
	public void fun(){
		System.out.println("父类方法");
	}
}
public class Son extends Father{

	String sonAttr = "子类属性";
	
	public void sonMethod(){
		System.out.println("子类方法");
	}
	
	@Override
	public void fun() {
		System.out.println("子类重写父类的方法");
	}
}

public class Test01 {
	public static void main(String[] args) {
		
		//向上转型
		Father father = new Son();
		
		System.out.println(father.fatherAttr);
		father.fatherMethod();
		
		father.fun();
	}
}

6.2向下转型

强制转型 - 向下转型:父类类型 转 子类类型

子类类型 变量名 = (子类类型) 父类变量名;

注意:

1.向下转型是不安全的 – ClassCastException类型转换异常,出现ClassCastException,一定要看错误信息

2.父类对象不能赋值给子类引用 – Dog dog = (Dog) new Animal();

3.向下转型之前必须先向上转型

4.向下转型之前,使用instanceof判断类型

public class Test01 {
	public static void main(String[] args) {
		
		//向上转型
		Animal an = new Cat();
		
		//向下转型
		if(an instanceof Dog){//判断引用an中指向的对象是否是Dog类型
			Dog dog = (Dog) an;
			dog.shout();
		}else if(an instanceof Cat){//判断引用an中指向的对象是否是Cat类型
			Cat cat = (Cat) an;
			cat.eat();
		}
		
	}
}

6.3对象转型的应用场景

public class MyString {

	private char[] value;
	
	public MyString(String original) {
		//"abc"
		//['a','b','c']
		value = original.toCharArray();//将字符串转换为字符数组
	}
	
	@Override
	public boolean equals(Object obj) {
		if(this == obj){
			return true;
		}
		
		if(obj instanceof MyString){
			MyString my = (MyString) obj;
			
			char[] v1 = this.value;
			char[] v2 = my.value;
			
			//比较字符长度
			if(v1.length != v2.length){
				return false;
			}
			
			for (int i = 0; i < v1.length; i++) {
				//比较字符的Unicode码是否相同
				if(v1[i] != v2[i]){
					return false;
				}
			}
			return true;
		}
		return false;
	}
	
	@Override
	public String toString() {
		return String.valueOf(value);//将字符数组转为字符串
	}
}
public class Test01 {
	public static void main(String[] args) {
		
		String str1 = new String("abc");
		
		System.out.println(str1.equals(new Student()));//false
		
		
		System.out.println("-------------------------------");
		
		MyString m1 = new MyString("abc");
		
		System.out.println(m1.equals(new Student()));//false
		
	}
}

7.内部类

理解:一个类中再声明另外一个类

将一个类A定义在另一个类B里面,里面的那个类A就称为内部类,B则称为外部类。

分类:

1.成员内部类:类定义在了成员位置 (类中方法外称为成员位置,无static修饰的内部类)

2.静态内部类:类定义在了成员位置 (类中方法外称为成员位置,有static修饰的内部类)
3.接口内部类
4.局部内部类:类定义在方法内
5.匿名内部类:没有名字的内部类,可以在方法中,也可以在类中方法外

应用场景:

1.B类的对象只在A类中使用,并且B类对象使用到了A类所有的属性,就可以将B类作为A类的成员内部类
2.B类的对象只在A类中使用,并且B类对象使用到了A类静态的属性,就可以将B类作为A类的静态内部类
3.抽象类的子类只创建一次对象,就没必要创建子类,直接使用匿名内部类(new 抽象类)
4.接口的实现类只创建一次对象,就没必要创建实现类,直接使用匿名内部类(new 接口)

public interface I1 {

	//接口内部类
	class Inner{
	}
}
//外部类
public class Outter {

	//成员内部类
	class Inner01{
	}
	
	//静态内部类
	static class Inner02{
	}
	
	public void method(){
		
		//局部内部类
		class Inner03{
		}
		
	}	
	
}

7.1成员内部类

需求:创建成员内部类的对象,操作对象的方法

成员内部类对象的创建格式:

内部类 变量 = new 外部类().new 内部类();

小结:

1.创建成员内部类对象之前,必须创建外部类对象

2.成员内部类可以调用外部类所有的属性

3.在成员内部类中调用指定的外部类属性:外部类.this.属性

//外部类
public class Outter {
	
	private 		String str1 = "外部类属性1";
					String str2 = "外部类属性2";
	protected 		String str3 = "外部类属性3";
	public 			String str4 = "外部类属性4";
	final 			String str5 = "外部类属性5";
	static 			String str6 = "外部类属性6";
	static final 	String str7 = "外部类属性7";

	//成员内部类
	class Inner{
		
		String str1 = "内部类属性";
		
		public void innerMethod(){
			System.out.println("成员内部类的方法");
			System.out.println(str1);//this.str1
			System.out.println(Outter.this.str1);
			System.out.println(str2);//Outter.this.str2
			System.out.println(str3);//Outter.this.str3
			System.out.println(str4);//Outter.this.str4
			System.out.println(str5);//Outter.this.str5
			System.out.println(str6);//Outter.str6
			System.out.println(str7);//Outter.str7
		}
	}
	
}
public class Test01 {
	public static void main(String[] args) {
		
		//创建成员内部类的对象
		Inner inner = new Outter().new Inner();
		
		//调用方法
		inner.innerMethod();
	}
}

7.3静态内部类

需求:创建静态内部类的对象,操作对象的方法

静态内部类对象的创建格式:

外部类.内部类 变量 = new 外部类.内部类构造器;

小结:

1.创建静态内部类对象,不用创建外部类对象

2.静态内部类只能调用外部类静态的属性

//外部类
public class Outter {
	
	static 			String str1 = "外部类属性1";
	static final 	String str2 = "外部类属性2";

	//静态内部类
	static class Inner{
		
		
		public void innerMethod(){
			System.out.println("静态内部类的方法");
			System.out.println(str1);//Outter.str1
			System.out.println(str2);//Outter.str2
		}
	}
	
}
public class Test01 {
	public static void main(String[] args) {
		
		//创建静态内部类的对象
		 Inner inner = new Outter.Inner();
		
		//调用方法
		inner.innerMethod();
	}
}

7.4接口内部类

需求:创建静态内部类的对象,操作对象的方法

小结:

接口内部类的使用方式和静态内部类一致

//外部接口
public interface Outter {
	
	//接口内部类
	//默认使用public static修饰
	class Inner{
		
		public void innerMethod(){
			System.out.println("接口内部类的方法");
		}
	}
	
}
public class Test01 {
	public static void main(String[] args) {
		
		//创建静态内部类的对象
		 Inner inner = new Outter.Inner();
		
		//调用方法
		inner.innerMethod();
	}
}

7.5局部内部类

需求:调用局部内部类的方法

小结:

1.局部内部类不能使用访问修饰符

2.局部内部类的作用域就在外部类方法中

//外部类
public class Outter {
	
	public void method(){
		
		//局部内部类
		class Inner{
			public void innerMethod(){
				System.out.println("局部内部类的方法");
			}
		}
		
		//创建局部内部类对象
		Inner inner = new Inner();
		//调用方法
		inner.innerMethod();
		
	}
	
}
public class Test01 {
	public static void main(String[] args) {
		
		Outter outter = new Outter();
		outter.method();
	}
}
7.5.1局部内部类的面试题

面试题:局部内部类使用到外部类的局部变量时,为什么局部变量会变为常量

答:局部变量变成常量,是让该变量的生命周期变长,使得方法以外还能找的到该数据,如果该变量时局部变量,方法执行完毕就直接被回收,在方法就不能使用该数据

//外部类
public class Outter {
	
	public Object method(){
		
		int num = 100;
		
		//局部内部类
		class Inner{
			@Override
			public String toString(){
				return "局部内部类的方法 -- " + num;
			}
		}
		
		Object obj = new Inner();
		
		return obj;
	}
	
}
public class Test01 {

	public static void main(String[] args) {
		
		Outter out = new Outter();
		
		Object obj = out.method();
		System.out.println(obj.toString());//调用子类重写的toString
	}
}

局部内部类理解图:

在这里插入图片描述

常量和局部变量的生命周期?

常量:存放在常量池中,项目销毁时,常量才会被回收
局部变量:调用方法,方法在栈中开辟空间,用于存放局部变量,方法执行完毕,该空间会立刻回收
意味着方法结束,局部变量也结束了

7.6匿名内部类

匿名内部类前提:

匿名内部类必须****继承一个父类*或者*实现一个父接口****

匿名内部类格式

new 父类名或者接口名(){

      // 方法重写

      @Override 

      public void method() {

      // 执行语句

    }

};

创建匿名内部类的对象
1.底层创建一个匿名类(Test01$1.class),继承A类,重写method方法
2.创建匿名子类的对象
3.赋值给父类的引用(类的多态)

public abstract class A {

	public abstract void method();
}
public class Test01 {
	public static void main(String[] args) {
		
		//创建匿名内部类的对象
		//1.底层创建一个匿名类(Test01$1.class),继承A类,重写method方法
		//2.创建匿名子类的对象
		//3.赋值给父类的引用(类的多态)
		A a = new A() {
			@Override
			public void method() {
			}
		};
		
		a.method();
		
	}
}
7.6.1匿名内部类(接口)

创建匿名内部类的对象
1.底层创建一个匿名类(Test01$1.class),实现I1接口,重写method方法
2.创建匿名实现类的对象
3.赋值给接口的引用(接口的多态)

public interface I1 {

	public void method();
}
public class Test01 {
	public static void main(String[] args) {
		
		//创建匿名内部类的对象
		//1.底层创建一个匿名类(Test01$1.class),实现I1接口,重写method方法
		//2.创建匿名实现类的对象
		//3.赋值给接口的引用(接口的多态)
		I1 i1 = new I1() {
			@Override
			public void method() {
				
			}
		};
		
		i1.method();
	}
}

简答题

1.instanceof 关键字的作用

instanceof 严格来说是Java中的一个双目运算符,用来测试一个对象是否为一个类的实例,用法为:

boolean result = obj instanceof Class

其中 obj 为一个对象,Class 表示一个类或者一个接口,当 obj 为 Class 的对象,或者是其直接或

间接子类,或者是其接口的实现类,结果result 都返回 true,否则返回false。

注意:编译器会检查 obj 是否能转换成右边的class类型,如果不能转换则直接报错,如果不能确定

类型,则通过编译,具体看运行时定。

int i = 0;
System.out.println(i instanceof Integer);//编译不通过 i必须是引用类型,不能是基本类型
System.out.println(i instanceof Object);//编译不通过
Integer integer = new Integer(1);
System.out.println(integer instanceof Integer);//true
//false ,在 JavaSE规范中对 instanceof 运算符的规定就是:如果 obj 为 null,那么将返回false。
System.out.println(null instanceof Object);
2.抽象类和接口的对比

抽象类是用来捕捉子类的通用特性的。接口是抽象方法的集合。

从设计层面来说,抽象类是对类的抽象,是一种模板设计,接口是行为的抽象,是一种行为的规范。

相同点

接口和抽象类都不能实例化

都位于继承的顶端,用于被其他实现或继承

都包含抽象方法,其子类都必须覆写这些抽象方法

不同点

参数抽象类接口
声明抽象类使用abstract关键字声明接口使用interface关键字声明
实现子类使用extends关键字来继承抽象类。如果子类不是抽象类的话,它需要提供抽象类中所有声明的方法的实现子类使用implements关键字来实现接口。它需要提供接口中所有声明的方法的实现
构造器抽象类可以有构造器接口不能有构造器
访问修饰符抽象类中的方法可以是任意访问修饰符接口方法默认修饰符是public。并且不允许定义为 private 或者 protected
多继承一个类最多只能继承一个抽象类一个类可以实现多个接口
字段声明抽象类的字段声明可以是任意的接口的字段默认都是 static 和 final 的

备注:Java8中接口中引入默认方法和静态方法,以此来减少抽象类和接口之间的差异。

现在,我们可以为接口提供默认实现的方法了,并且不用强制子类来实现它。

接口和抽象类各有优缺点,在接口和抽象类的选择上,必须遵守这样一个原则:

行为模型应该总是通过接口而不是抽象类定义,所以通常是优先选用接口,尽量少用抽象类。

选择抽象类的时候通常是如下情况:需要定义子类的行为,又要为子类提供通用的功能。

3.普通类和抽象类有哪些区别?

普通类不能包含抽象方法,抽象类可以包含抽象方法。

抽象类不能直接实例化,普通类可以直接实例化。

1.当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?

是值传递。Java 语言的方法调用只支持参数的值传递。当一个对象实例作为一个参数被传递到方法中时,参数的值就是对该对象的引用。对象的属性可以在被调用过程中被改变,但对对象引用的改变是不会影响到调用者的

2.为什么 Java 中只有值传递

按值调用表示方法接收的是调用者提供的值,而按引用调用表示方法接收的是调用者提供的变量地址。一个方法可以修改传递引用所对应的变量值,而不能修改传递值调用所对应的变量值。它用来描述各种程序设计语言(不只是 Java)中方法参数传递方式。

Java程序设计语言总是采用按值调用。也就是说,方法得到的是所有参数值的一个拷贝,也就是说,方法不能修改传递给它的任何参数变量的内容。*

总结

Java程序设计语言对对象采用的不是引用调用,实际上,对象引用是按值传递的。

下面再总结一下Java中方法参数的使用情况:

一个方法不能修改一个基本数据类型的参数(即数值型或布尔型)

一个方法可以改变一个对象参数的状态。

一个方法不能让对象参数引用一个新的对象。

3.值传递和引用传递有什么区别

值传递:指的是在方法调用时,传递的参数是按值的拷贝传递,传递的是值的拷贝,也就是说传递后就互不相关了。

引用传递:指的是在方法调用时,传递的参数是按引用进行传递,其实传递的引用的地址,也就是变量所对应的内存空间的地址。传递的是值的引用,也就是说传递前和传递后都指向同一个引用(也就是同一个内存空间)。

总结

1.抽象类及抽象方法

2.接口

3.思考题:抽象类与接口的区别

4.多态 – 重要!!!

5.对象转型(向上转型、向下转型)

6.内部类(成员内部类,静态内部类,接口内部类,局部内部类,匿名内部类)
1.注重使用
2.注重局部内部类的面试题
3.注重匿名内部类的内存图

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

A 北枝

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值