黑马程序员--Java基础之面向对象总结(二)

------- android培训java培训、期待与您交流! ----------
      接口的概述:接口是一个特殊的抽象类,当抽象类中的方法都是抽象的,那么该类可以通过接口的形式来表示。class用来定义类,interface用来定义接口,形式为interface 接口名称。定义接口时,接口中定义的成员变量是静态终例,成员方法是静态的抽象方法,并且成员都是公共的public,它们都是这样固定的修饰符来修饰,成员变量:public static final int x=0;,成员方法:public  abstract void show();。接口不可以被实例化,因为它具有抽象方法,只能被子类实现后,实例化这个子类来使用,子类实现接口时必须实现接口的所有抽象方法,否则子类是一个抽象类。接口可以被类多个实现,形式为interface C implements A,B{}。不同的接口拥有相同的抽象方法时,任然可以被一个类同时实现,因为接口中的抽象方法没有方法主体,可以由实现接口的类实现方法,并且只有这一个方法,同时需要注意的是,这几个相同的方法必须返回类型相同。几个接口之间可以实现多继承,例如interface A{},interface B{},interface C extends A,B{}
      接口的特点和示例:接口是对外暴露的规则,它是程序的功能的扩展。接口和类的关系是实现,类可以继承一个类的同时实现多个接口。打比方说接口就像是笔记本电脑上的usb接口,不同的对象使用这个接口会实现不一样的方法效果,使用该接口的对象实现了接口里面的方法。
abstract class Students {// 抽象类Students,提供公共方法sleep和抽象方法study
	public abstract void study();// 抽象方法用于被继承Students的子类实现,因为每个子类有不同的学习方法

	public void sleep() {// 公共方法sleep,所有继承Students的子类都拥有共同的睡觉方法。
		System.out.println("sleep...");
	}
}

interface Smoking {// 接口Smoking,定义的抽烟功能,不是所有人都抽烟,不能定义在抽象类Students中,谁抽烟谁实现这个接口
	public abstract void smoke();// 抽象方法smoke,谁抽烟抽什么烟,它就自己实现这个接口
}

class Zhangsan extends Students implements Smoking {// 定义的Zhangsan这个人的类继承自Students类,实现了Smoking接口,若其不抽烟则不实现抽烟的接口

	@Override
	public void study() {// 重写父类Students的study方法,定义自己学习的内容
		System.out.println("study Java...");
	}

	public void smoke() {// 实现接口Smoking的smoke方法,定义自己抽烟的方法
		System.out.println("smoking NanJing...");
	}

}

class TestInterface {
	public static void main(String[] args) {
		Zhangsan zs = new Zhangsan();// 实例化Zhangsan对象
		zs.study();// 调用每个学生都有但是各不相同的study方法
		zs.smoke();// 调用某些抽烟同学才有的smoke方法
		zs.sleep();// 调用所有学生都相同的sleep方法
	}
}
      多态的概述和扩展性:多态可以理解为一种事物存在着多种表现形态,比如动物这种事物,它具有很多的不同形态的动物,有猫狗等,人这个事物有学生老师等。在java里用面向对象的知识来表现可以这样,动物 a=new 猫()。多态的体现是父类的引用指向了自己的子类对象,同时父类的引用也可以接受自己的子类对象,即Animal cat=new Cat();此时Animal这个父类的引用指向了它的子类 Cat对象,方法function(Animal animal){}的参数是父类Animal,在使用这个方法时可以传递它的子类,如function(new Cat()),这就是父类的引用可以接受自己的子类对象。多态的前提是类和类之间有关系,这个关系可以是继承也可以是实现,同时它们之间存在覆盖关系,即父类中存在方法被子类覆盖,若类和类之间没有关系则不能实现多态,若不存在方法的覆盖那么多态也没有意义。多态的好处是极大程度地提高了程序的扩展性,但同时它的弊端就是只能使用父类的引用访问父类中的成员,如function(Animal animal){animal.eat();}只能使用父类animal对象的引用来访问父类中的成员。
abstract class Animal {// 抽象的父类Animal
	abstract void eat();// 抽象方法eat
}

class Cat extends Animal {// 继承父类Animal的子类Cat,Animal事物的一种多态表现
	@Override
	void eat() {// 覆盖父类的eat方法
		System.out.println("吃鱼...");
	}

	void catchMouse() {// 自己的catchMouse方法
		System.out.println("抓老鼠...");
	}
}

class Dog extends Animal {// 继承父类Animal的子类Dog,Animal事物的一种多态表现
	@Override
	void eat() {// 覆盖父类的eat方法
		System.out.println("吃骨头");
	}

	void lookHome() {// 自己的lookHome方法
		System.out.println("看守家...");
	}
}

class AnimalDemo {
	public static void main(String[] args) {
		animalEat(new Cat());// 调用Cat子类的eat方法
		animalEat(new Dog());// 调用Dog子类的eat方法
	}

	// 使用面向对象的多态特性抽取的公共方法,提高程序的扩展性,无论后期增加多少Animal的子类,这个方法都可以调用相应的eat方法
	public static void animalEat(Animal animal) {// 抽取Animal的eat方法,传递参数为Animal的多态形式下的子类,父类的引用接受子类对象
		animal.eat();
	}
}
      多态的转型:多态转型时分为俩种情况,一种是类型自动转换即类型提升,这是向上转型,例如父类初始化时对象引用指向子类,Animal a=new Cat()此时就是cat类型向上转型成Animal,a只有Animal中的eat方法,没有Cat的catchMouse方法,另一种是类型的强制转换即向下转型,例如Cat c=(Cat)a,此时将Animal父类强制转换成子类Cat,c有eat和catchMouse方法。当animalEat(Animal animal)方法中存在不同子类对象自己独有的方法调用时,我们需要进行类型判断再调用相应方法,使用关键字instanceof,父类 instanceof 子类,最后结果是boolean类型,判断父类类型是否属于这个子类类型,如果是那么强制转换成这个类型,如果不是强制转换就会报类型转换的异常。
	public static void animalEat(Animal animal) {
		animal.eat();
		if (animal instanceof Cat) {// 判断animal是否属于Cat类型
			Cat c = (Cat) animal;// 强转换成Cat类型
			c.catchMouse();// 调用Cat中自己的catchMouse方法
		} else if (animal instanceof Dog) {// 判断animal是否属于Dog类型
			Dog d = (Dog) animal;// 强转换成Dog类型
			d.lookHome();// 调用Dog中自己的lookHome方法
		}
	}
      多态中成员的特点:当实现多态的语句时,jvm先进行编译,编译成功后再运行,例如:父类Fu中有method1和method2方法,子类Zi中覆盖了method1方法,具有自己的method3方法。实现以下多态代码段,Fu f=new Zi();f.method1();f.method2();f.method3();首先编译不通过,报f类中没有method3这个方法的错误,的确父类Fu中没有方法method3,当去掉调用method3这个方法后编译成功,然后运行时,程序执行的是调用子类Zi的method1方法和调动的父类的method2方法。在编译时期jvm参阅引用变量类型所属类中是否有调用的方法,如果有则编译通过,如果没有则编译不通过,这里编译时就先检查父类Fu是否有所需要调用的方法。在运行时期jvm参阅对象所属类型的类中是否有调用的方法,这里运行时就检查对象子类Zi中是否覆盖了父类的方法,method1覆盖了父类的方法所以调用子类的method1方法,method2没有覆盖父类的方法所以调用父类的method2方法。简单对它们总结就是,成员函数在多态调用时,编译看左边,运行看右边。在多态中成员变量的特点是,不管是编译还是运行都看左边,例如父类Fu{int num=5;},子类Zi{int num=8},Fu f=new Zi();Zi z=new Zi();此时f.num的结果是5,仍是父类Fu的成员变量,z.num的结果是8,是子类Zi本身的成员变量。多态中的静态成员函数的特点是无论编译和运行都看左边,例如父类Fu中是static的method1,子类Zi中是static的method1,Fu f=new Zi();f.method1();此时调用结果是调用了父类的method1方法。因为静态方法的调用都是用类名直接调用,虽然使用了实例化出的对象来调用,但是存储在内存中方法区的静态方法都被绑定在所属类上,使用时就是用类名调用的,非静态方法自然就是使用该对象来调用,所以调用的是对象中的方法。
      动态绑定和静态绑定:Fu f=new Zi();f.method1();Zi z=new Zi();z.method1();当父类中的method1方法是静态时,子类继承后覆盖静态method1方法,那么方法加载在内存中的方法区时就是静态绑定,静态绑定形式为Fu.method1(),使用时虽然形式上是f.method1()但实际就是引用类型的类名调用方法,即Fu.method1(),z.method1()实际就是引用类型的类名Zi调用方法method1。当method方法为非静态时,方法加载在内存中的方法区时就是动态绑定,动态绑定形式为this.method1(),那么f.method1()实际就是对象f调用它的方法,即new Zi().method1()。
      多态的主板示例:一个主板是电脑使用的核心,当使用电脑运行某些方法时需要使用主板来运行,主板先运行然后再运行你要执行的操作,比如上网和放音乐,那么主板就需要执行网卡和声卡,早期网卡和声卡这种都是焊死在主板上,若需要更换或者升级等非常不方便,后期主板上出现了PCI接口,这个接口就是一种协议标准,它是连接到主板的核心,网卡和声卡制造时按照这个接口标准实现该接口就可以和主板相连使用,那么不管是什么类型的设备,不管是同种设备的不同型号,它们只要插在主板的这个接口上就能被使用,降低了主板和各种设备的耦合性,易于设备的更新和维护。我们代码实现时就在主板类中定义使用该接口的方法,将接口作为参数传递过去,即插上网卡就使用上网功能,插上声卡就能实现播放音乐功能。这时接口引用指向了实现该接口的子类对象,这里就是多态的应用。
public class MainBord {// 主板类,电脑运行时的核心
	public void run() {// 主板运行的方法
		System.out.println("MainBord run...");
	}

	public void usePCI(PCI p) {// 主板使用PCI接口槽,若有设备插入则运行,若没有则不运行
		if (null != p) {// 判断有设备运行的方法,没有设备则不执行方法,非空判断保证没有设备电脑也能正确运行不报错
			p.open();
			p.close();
		}
	}
}

interface PCI {// PCI接口类,实现该接口的设备都可以插在电脑上运行
	public void open();

	public void close();
}

class NetCard implements PCI {// 制造的网卡设备,实现PCI接口
	public void open() {
		System.out.println("NetCard open...");
	}

	public void close() {
		System.out.println("NetCard close...");
	}
}

class ComputerDemo {// 模拟的电脑类
	public static void main(String[] args) {
		MainBord m = new MainBord();// 电脑运行核心是运行主板,实例化主板对象
		m.run();// 主板运行
		m.usePCI(null);// 没有设备也能运行
		m.usePCI(new NetCard());// 主板执行PCI接口,插的是什么设备则运行什么设备,若有多个设备则一起运行
	}
}
      Object类和equals()方法:Object类是所有类的超类,有一种说法叫做上帝类,它没有父类,所有的类都是继承于它,具备它所有的方法。equals方法就是Object类中用来比较俩个对象是否相等的方法,它是比较两者的内容,如果比较的俩者都是基本数据类型则比较俩者的内容,如果比较对象是类对象则是比较俩者的存储地址。当定义一个类后若实例化2个对象,例如Demo d1=new Demo();Demo d2=new Demo();d1.equals(d2);那么它的比较结果是false,因为他们都是类对象,比较的是对象的地址,必然不同时false,若类Demo中定义int num;属性,希望使用equals比较时是比较这个属性,那么就需要在定义Demo类时重写它继承子超类Object的equals方法,这样就能实现自定义的比较方法。
class Demo {// 定义Demo类
	int num;

	Demo(int num) {
		this.num = num;
	}

	@Override
	// 重写Object类的equals方法,实现它的自定义比较方法
	public boolean equals(Object obj) {
		if (!(obj instanceof Demo)) {// 当传递的对象不属于Demo类时直接返回false
			return false;
		}
		Demo d = (Demo) obj;// 将传递的obj对象向下转型成Demo,这样它才具备num属性,否则obj没有改属性
		return num == d.num;// 将当前对象的num属性和传递的obj的属性比较
	}
}

class People {
}

public class Object_equals {
	public static void main(String[] args) {
		Demo d1 = new Demo(2);
		Demo d2 = new Demo(2);
		System.out.println(d1.equals(d2));// 自定义的equals方法比较,实际是比较的是两者的num属性,若没有覆盖方法则是比较俩者的地址
		System.out.println(d1.equals(new People()));// 当比较对象不是Demo型时,通过定义方法里的类型判断,返回值为false
	}
}
      内部类访问规则:将一个类定义在一个类中,这个类就叫做内部类。内部类的访问规则是:内部类可以直接访问外部类的成员,包括私有的,之所以能够访问是因为内部类中有一个外部类的引用,格式为外部类名.this;外部类要访问内部类中的内容必须要建立内部类的对象。
public class Outer {// 定义外部类
	private int x = 3;// private修饰的成员属性x
	private int y = 8;

	class Inner {// 定义内部类
		private int y = 1;

		void show() {
			System.out.println("Inner:" + x);// 内部类可以访问外部类的private修饰的属性,它前面省略了外部类名.this,如Outer.this.x
			System.out.println("Inner:" + y);// 当内部类存在同名的成员时y是内部类的y,值为1,它类似于普通类,省略了this,如this.y
		}
	}

	private class InnerMethod {
		// 内部类属于外部类Outer的成员,所以内部类能够被private修饰,注意,一般类是不能被private修饰的
	}
}

class InnerClassDemo {
	public static void main(String[] args) {
		Outer.Inner in = new Outer().new Inner();// 使用内部类时必须使用外部类的引用
		in.show();
	}
}
      静态内部类的使用:在外部类中的成员位置上就可以被成员修饰符所修饰,所以内部类可以被private和static修饰,private是将内部类在外部类中封装,static修饰的内部类具有static的特性,static静态内部类只能访问外部类的静态成员。注意,内部内具有静态方法时该内部类必须定义为静态内部类,当外部类的静态方法访问内部类时,该内部类也必须是静态内部类。
public class OuterClass {
	static void methodOuter() {// 外部类的静态方法
		System.out.println("OuterClass...methodOuter");
		// InnerClass.methodInner();外部类的静态方法访问的内部类必须是static静态的
	}

	static class InnerClass {// 当存在静态方法时,内部类必须定义成静态的
		static void methodInner() {
			System.out.println("InnerClass...methodInner");
			methodOuter();// 静态的内部类只能访问外部类的静态成员(变量、方法)
		}
	}

	public static void main(String[] args) {
		OuterClass.InnerClass.methodInner();// 访问静态内部类的静态方法的形式
		// 若InnerClass为非静态方法,访问形式为new OuterClass.InnerClass().methodInner();
	}
}

      匿名内部类:内部类定义在局部时,不可以被成员修饰符修饰,如不可以被static修饰,可以直接访问外部类中的成员,因为还持有外部类中的引用,但是不可以访问它所在的局部中的变量,只能访问被final修饰的局部变量。匿名内部类其实就是内部类的简写格式,定义匿名内部类的前提是内部类必须是集成一个类或者实现一个接口,匿名内部类的格式:new 父类或者接口名{定义的子类内容},其实匿名内部类就是一个匿名子类的对象,可以理解为带内容的对象,并且为了简化代码的书写,内部类中定义的方法最好不要超过3个。

interface Demo {// 定义的Demo接口
	void method();
}

public class Outer {// 定义的外部类Outer
	static Demo speak() {// 外部类的静态方法speak,返回类型为Demo接口
		final int num = 5;// 局部变量num类型为final
		return new Demo() {// 返回一个Demo接口对象,该对象是匿名内部类,实际是实现Demo接口的子对象
			public void method() {// 实现Demo接口必须重写的方法method
				System.out.println("Demo:.." + num);// 局部定义的内部类访问局部变量时只能访问final修饰的变量,所以参数为final,同时它默认可访问外部类的对象
			}
		};
	}

	// 注意:局部定义的内部类不在外部类的成员位置,所以它不能有成员修饰符,例如private,static,因此它不能够包含static的静态方法
	static class InnerDemo implements Demo {// 实现以上类似方法,使用不匿名内部类,则需要先定义静态的实现Demo接口的类
		int num = 5;

		public void method() {
			System.out.println("Demo:..." + num);
		}
	}

	static Demo speak1() {
		Demo d = new InnerDemo();// 先实例化实现Demo接口的对象
		return d;
	}
}

class InnerTest {
	public static void main(String[] args) {
		Outer.speak().method();// 调用Outer的静态方法speak,它的返回值是Demo接口类型的对象,然后调用该对象的method方法
		Demo d = Outer.speak1();// 效果等同以下方式
		d.method();
		// 面试题要使用一个匿名内部类的任意方法,但这个类没有继承或者实现接口
		new Object() {// 所有类都父类object,所以该匿名内部类其实是object的子类对象
			void function() {
			}
		};
	}
}
      自定义异常:项目中可能会出现一些问题,这些问题没有被java描述封装,我们要解决这类问题就需要自定义异常类来封装描述它们。当在函数出现了throw抛出的异常时,就必须给出对应的处理,这里有俩种解决方法,一种是在函数内部使用try catch处理,一种是在函数申明上使用thorws向上抛出,让调用者处理。在java运行的主函数内使用try语句处理异常,或者继续向上抛异常,最终处理异常的是jvm。自定义异常时,必须继承Exception类,因为这个体系具有一个特点,异常类和异常对象都被抛出,他们都具有可抛性,这是Throwable体系的独有特点。因为父类把异常信息的操作都完成了,所以子类在构造时只需要将异常信息传递给父类,使用super语句传递参数并构造,通过getMessage方法就可以获取自定义的异常信息。
public class MinusException extends Exception {// 自定义异常,继承Exception类
	public MinusException(String msg) {// 带参构造参数,使用super语句将异常信息传递给父类构造函数,便于使用Exception共有的方法获取异常信息
		super(msg);
	}
}

class MinusDemo {// 测试负数方法的类
	public int div(int a, int b) throws MinusException {// 除法运算的函数,抛出了自定义异常
		if (b < 0) {
			throw new MinusException("除数出现负数了/ by minus");// 函数中手动抛出负数自定义异常
		}
		return a / b;
	}
}

class ExceptionDemo {
	public static void main(String[] args) {
		MinusDemo md = new MinusDemo();
		try {// 使用try语句块解决div方法申明的异常
			md.div(4, -1);
		} catch (MinusException e) {
			System.out.println(e.toString());// 打印自定义异常处理的信息
		} finally {
			System.out.println("finally");// finally中存放的是一定会被执行的代码,通常用于关闭资源
		}
	}
}
      throw和throws的区别:throw使用在函数上,throws使用在函数内,throws后面跟的异常类,可以跟多个,用逗号隔开,throw后跟的是异常对象。
      RuntimeException异常:Exception中有一个特殊的子类异常RuntimeException运行时异常,如果在函数内容内抛出该异常,函数上可以不用申明,编译能够通过,如果在函数上申明了该异常,调用者可以不用处理,编译能够通过。自定义异常时,如果该异常的发生,无法再继续进行运算,就让自定义异常继承RuntimeException。对于异常分为俩种:1,编译时被检测的异常。2,编译时不被检测的异常(运行时异常,RuntimeException及其子类)。
      覆盖时异常的特点:1,子类在覆盖父类的方法时,如果父类抛出异常,那么子类的覆盖方法只能抛出父类的异常或者该异常的子类。2,如果父类抛出多个异常,那么子类在覆盖方法时只能抛出父类异常的子集。例如父类抛出3个异常,子类只能抛出小于等于3个的异常。3,如果父类中没有异常抛出,那么子类覆盖方法时也不可以抛出异常。如果子类发生了异常,就必须使用try语句进行处理,绝对不能抛出异常。
      包与包之间的访问权限:类的访问权限只有俩种,为默认权限friendly和公共权限public。默认权限不同包是不能访问的,必须改为公共权限才能实现不同包中类的访问。对于类中的成员(成员变量和成员方法)有四种访问权限,不同的访问权限则在不同情况下才能访问。这四种访问权限从小到大分别为私有权限private,默认权限friendly,保护权限protected,公共权限public,下面用表格表示这4种权限的访问情况。
四种访问权限形式
 privatefriendlyprotectedpublic
同一个类
同一个包X
不同包中的子类XX
不同的包XXX

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值