多态:一个对象具备多种形态。(父类的引用类型变量指向了子类的对象)
或者是接口 的引用类型变量指向了接口实现类的对象)
运行时多态存在的三个必要条件:
- 要有继承(包括接口的实现);
- 要有重写;
- 父类引用指向子类对象。
父类 a = new 子类();
运行时多态的解释:
1.运行时多态是指程序中定义的引用变量所指向的具体类型
2.通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定.
多态的好处:
1.可替换性(substitutability)。多态对已存在代码具有可替换性。例如,多态对圆Circle类工作,对其他任何圆形几何体,如圆环,也同样工作。
2.可扩充性(extensibility)。多态对代码具有可扩充性。增加新的子类不影响已存在类的多态性、继承性,以及其他特性的运行和操作。实际上新加子类更容易获得多态功能。例如,在实现了圆锥、半圆锥以及半球体的多态基础上,很容易增添球体类的多态性。
3.接口性(interface-ability)。多态是超类通过方法签名,向子类提供了一个共同接口,由子类来完善或者覆盖它而实现的。超类Shape规定了两个实现多态的接口方法,computeArea()以及computeVolume()。子类,如Circle和Sphere为了实现多态,完善或者覆盖这两个接口方法。
4.灵活性(flexibility)。它在应用中体现了灵活多样的操作,提高了使用效率。
5.简化性(simplicity)。多态简化对应用软件的代码编写和修改过程,尤其在处理大量对象的运算和操作时,这个特点尤为突出和重要。
多态的应用:
- 多态用于形参类型的时候,可以接收更多类型的数据 。
- 多态用于返回值类型的时候,可以返回更多类型的数据。
多态要注意 的细节:
1. 多态情况下,子父类存在同名的成员变量时,访问的是父类的成员变量。
2. 多态情况下,子父类存在同名的非静态的成员函数时,访问的是子类的成员函数。
3. 多态情况下,子父类存在同名的静态的成员函数时,访问的是父类的成员函数。
4. 多态情况下,不能访问子类特有的成员。
总结:多态情况下,子父类存在同名的成员时,访问的都是父类的成员,除了在同名非静态函数时才是访问子类的。
编译看左边,运行不一定看右边。
编译看左边:java编译器在编译的时候,会检查引用类型变量所属的类是否具备指定的成员,如果不具备马上编译报错
- 成员内部类
第一种:内部类:
一个类定义在另外一个类的内部,那么该类就称作为内部类。
内部类的class文件名: 外部类$内部类. 好处:便于区分该class文件是属于哪个外部类的。
内部类的类别:
成员内部类:
成员内部类的访问方式:
1.在外部类提供一个方法创建内部类的对象进行访问。
2.在其他类直接创建内部类的对象。 格式:
外部类.内部类 变量名 = new 外部类().new 内部类();
注意: 如果是一个静态内部类,那么在其他类创建 的格式:
外部类.内部类 变量名 = new 外部类.内部类();
内部类的应用场景: 我们在描述A事物的时候,发现描述的A事物内部还存在另外一个比较复杂的事物B时候,而且这个比较复杂事物B还需要访问A事物的属性等数据,那么这时候我们就可以使用内部类描述B事物。
内部类的好处:内部类可以直接访问外部类的所有成员。
内部类要注意的细节:
- 如果外部类与内部类存在同名的成员变量时,在内部类中默认情况下是访问内部类的成员变量。
可以通过"外部类.this.成员变量名" 指定访问外部类的 成员。 - 私有的成员内部类只能在外部类提供一个方法创建内部类的对象进行访问,不能在其他类创建对象了。
- 成员内部类一旦出现了静态的成员,那么该类也必须 使用static修饰。
第二种:局部内部类
局部内部类: 在一个类 的方法内部定义另外一个类,那么另外一个类就称作为局部内部类。
局部内部类要注意的细节:
如果局部 内部类访问了一个局部变量,那么该局部变量必须使用final修饰、
第三种:匿名内部类
:没有类名的类就称作为匿名内部类。
匿名内部类的好处:简化书写。
匿名内部类的使用前提:必须存在继承或者实现关系才能使用。
public class UserAccount {
public void print() {
/// 需求: 在方法内部定义一个类继承自Person类,然后执行eat和sleep方法,
/**
* 第一种方式:局部内部类的方式,此种方式比较麻烦
class Student extends Person {
public Student(String name) {
super(name);
// TODO Auto-generated constructor stub
}
@Override
public void eat() {
// TODO Auto-generated method stub
System.out.println(name +"吃饭");
}
@Override
public void sleep() {
// TODO Auto-generated method stub
System.out.println(name +"睡觉");
}
}
/// 创建对象调用run和sleep方法
Student student = new Student("杨孝远");
student.eat();
student.sleep();
*/
/*
* 第二种方式: 匿名内部类
*/
Person person = new Person("杨孝远") {
@Override
public void sleep() {
// TODO Auto-generated method stub
System.out.println(name +"睡觉觉");
}
@Override
public void eat() {
// TODO Auto-generated method stub
System.out.println(name +"池吃吃吃");
}
};
person.sleep();
person.eat();
}
}
/// 这样调用
public static void main(String[] args) {
UserAccount userAccount = new UserAccount();
userAccount.print();
}
匿名内部类一般是用于实参, 比如以下代码
public static void main(String[] args) {
test(new Dao() {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("跑跑跑跑跑");
}
});
}
public static void test(Dao dao) {
dao.run();
}
实现关系下匿名内部类
/// 实现关系下的匿名函数
// 警告: the type is already defined 原因:接口名与其他接口名存在冲突相同,修改下接口名即可
interface Dao {
public abstract void run();
}
public class User {
public void print() {
/// 匿名函数
new Dao() {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("跑啊跑");
}
}.run();;
}
}
疑问: 什么时候使用内部类呢?
当我们分析事物时,发现事物的内部还有具体的事物,这时则应该定义内部类了。
比如人体是一个类,人体有心脏,心脏的功能在直接访问人体的其他内容。这时就将心脏定义在人体类中,作为内部类存在。
内部类的优势:成员内部类作为外部类的成员,那么可以访问外部类的任意成员。
- java 异常处理 Throwable Error 和Exception
在Java中,根据错误性质将运行错误分为两类:错误和异常。
在Java程序的执行过程中,如果出现了异常事件,就会生成一个异常对象。生成的异常对象将传递Java运行时系统,这一异常的产生和提交过程称为抛弃(throw)异常。
当Java运行时系统得到一个异常对象时,它将会沿着方法的调用栈逐层回溯,寻找处理这一异常的代码。找到能够处理这类异常的方法后,运行时系统把当前异常对象交给这个方法进行处理,这一过程称为捕获(catch)异常。
Java中的所有异常都是由Throwable类的子类生成的对象,所有的异常类都是Throwable类的子类或子类的子类。Throwable类是Object类的直接子类,Error类和Exception类是Throwable类的两个直接子类。
Throwable常用的方法:
toString()
返回当前异常对象的完整类名+病态信息。
getMessage()
返回的是创建Throwable传入的字符串信息。
printStackTrace()
打印异常的栈信息。
比如以下代码:
public class LoginTool {
/// 登录时抛出异常
public void login(String user, String pwd) throws CustomException {
if (user.length() == 0 || pwd.length() == 0) {
/// 当账号或者密码为空时,就抛出异常
throw new CustomException("账号或密码为空,请重新输入后,在点击登录");
} else {
System.out.println("登录成功哦");
}
}
}
// 在处理异常时,可以直接直接捕获异常进行处理
public static void main(String[] args) { /// 直接将异常抛给jvm虚拟机处理
LoginTool loginTool = new LoginTool();
try {
loginTool.login("sey", "");
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace(); /// 打印异常抛出的堆栈信息
System.out.println(e.getMessage());
}
}
/// 也可以根据情况将异常继续往上抛出, 在方面声明后面throws CustomException
public static void main(String[] args) throws CustomException { /// 直接将异常抛给jvm虚拟机处理
LoginTool loginTool = new LoginTool();
loginTool.login("sey", "111");
}
java中一般异常和运行时异常的区别:
Java提供了两类主要的异常:runtime exception和checked exception。checked
异常也就是我们经常遇到的IO异常,以及SQL异常都是这种异常。对于这种异常,
JAVA编译器强制要求我们必需对出现的这些异常进行catch。所以,面对这种异常
不管我们是否愿意,只能自己去写一大堆catch块去处理可能的异常。
但是另外一种异常:runtime exception,也称运行时异常,我们可以不处理
。当出现这样的异常时,总是由虚拟机接管。比如:我们从来没有人去处理过
NullPointerException异常,它就是运行时异常,并且这种异常还是最常见的异
常之一。
出现运行时异常后,系统会把异常一直往上层抛,一直遇到处理代码。如果
没有处理块,到最上层,如果是多线程就由Thread.run()抛出,如果是单线程就
被main()抛出。抛出之后,如果是线程,这个线程也就退出了。如果是主程序抛
出的异常,那么这整个程序也就退出了。运行时异常是Exception的子类,也有一
般异常的特点,是可以被Catch块处理的。只不过往往我们不对他处理罢了。也就
是说,你如果不对运行时异常进行处理,那么出现运行时异常之后,要么是线程
中止,要么是主程序终止。
如果不想终止,则必须扑捉所有的运行时异常,决不让这个处理线程退出。
队列里面出现异常数据了,正常的处理应该是把异常数据舍弃,然后记录日志。
不应该由于异常数据而影响下面对正常数据的处理。在这个场景这样处理可能是
一个比较好的应用,但并不代表在所有的场景你都应该如此。如果在其它场景,
遇到了一些错误,如果退出程序比较好,这时你就可以不太理会运行时异常,或
者是通过对异常的处理显式的控制程序退出。
- finally关键字
finally 块;
finally块的 使用前提是必须要存在try块才能使用。
finally块的代码在任何情况下都会执行的,除了jvm退出的情况。
finally非常适合做资源释放的工作,这样子可以保证资源文件在任何情况下都 会被释放。
try块的三种组合方式:
第一种: 比较适用于有异常要处理,但是没有资源要释放的。
try{
可能发生异常的代码
}catch(捕获的异常类型 变量名){
处理异常的代码
}
第二种:比较适用于既有异常要处理又要释放资源的代码。
try{
可能发生异常的代码
}catch(捕获的异常类型 变量名){
处理异常的代码
}finally{
释放资源的代码;
}
第三种: 比较适用于内部抛出的是运行时异常,并且有资源要被释放。
try{
可能发生异常的代码
}finally{
释放资源的代码;
}
比如下面代码
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import javax.net.ssl.SSLContext;
public class FinallyDemo {
public void demo() throws IOException {
/// 在任何一个空闲的端口上创建一个套接字
ServerSocket serverSocket = new ServerSocket();
try {
Socket socket = serverSocket.accept();
} catch (Exception e) {
// TODO: handle exception
System.out.println("socket失败");
} finally {
serverSocket.close();
System.out.println("关闭成功");
}
}
}
这段代码创建了一个套接字,并调用 accept 方法。在退出该方法之前,您必须关闭此套接字,以避免资源漏洞。为了完成这一任务
finally 块确保 close 方法总被执行,而不管 try 块内是否发出异常。因此,可以确保在退出该方法之前总会调用 close 方法。这样您就可以确信套接字被关闭并且您没有泄漏资源。在此方法中不需要再有一个 catch 块。在第一个示例中提供 catch 块只是为了关闭套接字,现在这是通过 finally 关闭的。如果您确实提供了一个 catch 块,则 finally 块中的代码在 catch 块完成以后执行。
finally 块必须与 try 或 try/catch 块配合使用。此外,不可能退出 try 块而不执行其 finally 块。如果 finally 块存在,则它总会执行。(无论从那点看,这个陈述都是正确的。有一种方法可以退出 try 块而不执行 finally 块。如果代码在 try 内部执行一条 System.exit(0); 语句,则应用程序终止而不会执行 finally 执行。另一方面,如果您在 try 块执行期间拨掉电源,finally 也不会执行。)