多态
概念:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果,这就是多态性。简单的说:就是用基类的引用指向子类的对象。
特点:
1. 要有继承关系
2. 要有方法重写
3. 要有父类引用指向子类对象
成员变量:编译看左边(父类),运行看左边(父类)
成员方法:编译看左边(父类),运行看右边(子类)。动态绑定
静态方法:编译看左边(父类),运行看左边(父类)。静态和类相关,算不上重写
父类引用指向子类对象就是向上转型,还有向下转型:Cat c = (Cat) a;
关键字:instanceof 判断前边的引用是否是后边的数据类型
多态的好处:提高了代码的维护性(继承保证)
提高了代码的扩展性(由多态保证)。当作参数使用的时候最好,因为扩展性强。
多态的弊端:不能使用子类特有的属性和行为
抽象类、方法
概念:当无法描述一个方法时定义为抽象,加上关键字abstract:
抽象类:abstract class 类名{}
抽象方法:public abstract void eat{}
抽象类不一定有抽象方法,有抽象方法的类一定是抽象类或接口,抽象类不能实例化。
若要实例化抽象类;按照多态的方式,由具体的子类实例化。其实这也是多态的一种,抽象类多态。
抽象类的子类要么是抽象类,要么重写抽象类的所有抽象方法。
若没有定义抽象方法却定义了抽象类,其目的是不让其他类创建本类对象,交给子类完成。
Abstract不能与static共存:被abstract修饰的方法没有方法体,被static修饰的可以用类名.调用,但是类名.调用抽象方法是 没有意义的。
Abstract不能与final共存:被abstract修饰的方法强制子类重写,被final修饰的不让子类重写,所以他俩是矛盾。
Abstract不能与private共存:被abstract修饰的是为了让子类看到并强制重写,被private修饰的不让子类访问,所以他俩矛盾
接口interface
概念:对外提供规则的都是接口
特点:
1. 接口用关键字interface表示:interface 接口名{}
2. 类实现接口用implements表示:class 类名 implements 接口名{}
3. 接口不能实例化:按照多态的方式来实例化
4. 接口的子类可以是抽象类,但是意义不大;可以是具体类,要重写接口中的所有抽象方法
成员变量:只能是常量,并且是静态的并公共的。默认修饰符:public static final
构造放法;接口没有构造方法
成员方法:只能是抽象方法
类与类、类与接口、接口与接口的关系:
类与类:继承关系,只能单继承,可以多层继承
类与接口:实现关系,可以单实现,也可以多实现;还可以在继承一个类的同时实现多个接口
接口与接口:继承关系,可以单继承,也可以多继承
抽象类与接口的区别
1. 成员区别:
抽象类:成员变量:可以变量,也可以常量
构造方法:有
成员方法:可以抽象也可以非抽象
接口:成员变量:只可以常量
成员方法:只可以抽象
2. 关系区别:
类与类:继承、单继承
类与接口:实现,单继承,多实现
接口与接口:继承,单继承,多继承
抽象类:被继承体现的是:”is a”的关系。抽象类中定义的是该继承体系的共性功能。
接口类:被实现体现的是;”like a”的关系。接口中定义的是该继承体系的扩展功能
局部内部类
只能在其所在的方法中访问。局部内部类访问局部变量必须用final修饰
匿名内部类
就是内部类的简化写法。(写在方法里)
前提:存在一个类或接口。这里的类可以是具体类也可以是抽象类
格式:new 类名或者接口名(){重写方法;}
本质:是一个继承了该类或者实现了该接口的子类匿名对象
静态Static
静态是随着类的加载而加载,this是随着对象的创建而存在;静态比对象优先存在;
This调用本类成员变量,super调用父类成员变量
tring StringBuffer和StringBuilder的区别
StringBuffer是jdk1.0版本的,是线程安全的,效率低
StringBuilder是jdk1.5版本的,是线程不安全的,效率高
String是一个不可变的字符序列
JDK5新特性
自动装箱:把基本类型转换为包装类类型
自动拆箱:把包装类类型转换为基本类型
Integer ii = 100;//自动装箱
Ii + = 200;//自动拆箱
-128到127是byte的取值范围,如果在这个取值范围内,自动装箱就不会新创建对象,而是从常量池中获取,如果超过 了byte取值范围就会再创建新的对象
正则表达式
正则表达式:是指一个用来描述或者匹配一系列符合某个语法规则的字符串的单个字符串。其实就是一个规则。有自己特殊的应用
作用:比如注册邮箱,邮箱有用户名和密码,一般会对其限制长度,这个限制长度的事情就是正则表达式做的
集合框架
集合的由来:
数组长度是固定,当添加的元素超过了数组长度时需要对数组重新定义,太麻烦,java内部给我们提供了集合类,能存储任意对象,长度是可以改变的,随着元素的增加而增加
数组和集合的区别:
区别1:数组既可以存储基本数据类型(存储值),又可以存储引用数据类型(存储地址)
集合只能存储引用数据类型
区别2:数组长度是固定的,不能自动增长
集合的长度是可变的,可以根据元素的增加而增长
数组和集合什么时候用:
1.如果元素个数是固定的推荐用数组
2.如果元素个数不是固定的推荐用集合
List的三个子类的特点
ArrayList:底层数据结构是数组,查询快,增删慢;线程不安全,效率高
Vector:底层数据结构是数组,查询快,增删慢;线程安全,效率低
Vector相对ArryList查询慢(线程安全的)
Vector相对LinkList增删慢(数据结构)
LinkedList:底层数据结构是链表,查询慢,增删快;线程不安全,效率高
查询多用ArryList
增删多用LinkedList
如果都多用ArrayList
迭代器原理
Collection c = new ArryList();父类引用指向子类对象
Iterator it = c.iterator();获取迭代器
//ListIterator list = list.listIterator;获取迭代器(ListIterator特有)
迭代器原理:迭代器是对集合进行遍历,而每一个集合内部的存储结构都是不同的,所以每一个集合存和取都是不一样,那么需要在每一个类中定义hasNext()和next()方法,第二,代码有底层内部实现,使用者不用管怎么实现的,会用即可
并发修改异常:遍历集合的同时修改
数据结构之数组和链表
A. 数组:查询快修改也快;增删慢
B. 链表:查询慢修改野蛮;增删快
栈和队列数据结构
栈:先进后出
队列:先进先出
泛型概述
基本在类的旁边加<>,<>中放的必须是引用数据类型;前后的泛型必须一致,或者后面的泛型可以省略不写
好处:提高安全性,省去强转的麻烦
静态方法必须声明自己的泛型
HashSet如何保证元素唯一性的原理
1.HashSet原理:
我们使用Set集合都是需要去掉重复元素的,如果存储的时候逐个equals()比较,效率较低,哈希算法提高了去重复的效率,降低了使用equals()方法的次数
当HashSet调用add()方法存储对象的时候,先调用对象的hashcode()方法得到一个哈希值,然后在集合中查找是否有哈希值相同的对象
如果没有哈希值相同的对象就直接存入集合
如果有哈希值相同的对象逐个进行equals()比较,比较结果为false就存入,true则不存
2.将自定义类的对象存入HashSet去重复
类中必须重写hashCode()和equals()方法
HashCode():属性相同的对象返回值必须相同,属性不同的返回值尽量不同(提高效率)
Equals:属性相同返回true,属性不同返回flase,返回flase的时候存储
LinkedHashSet
底层是链表实现的,是Set集合中唯一一个能保证怎么存就怎么取的集合对象
因为是HashSet的子类,所以也是保证元素唯一的,与HashSet的原理一样
TreeSet保证元素唯一和自然排序
TreeSet集合是用来对元素进行排序的,同样也可以保证元素的唯一
需要重写compareTo方法
当compareTo方法返回0时,集合中只有一个元素
当compareTo方法返回正数的时候集合会怎么存就怎么取
当compareTo方法返回负数时,集合会倒叙存储
TreeSet底层时二叉树,两个叉,小的存储在左边(负数),大的存储在右边(正数),相等就不存在(0)
TreeSet原理
Map
双列集合
HashMap:底层是哈希算法,针对键
LinkedHashMap:底层是链表,针对键
TreeMap:底层是二叉树算法,针对键
开发中HashMap比较多
Map集合遍历
For(Map.Entry<String, Integer> en : map.entrySet()){ //键值对对象
System.out.println(en.getKey() + “=” + en.getValue());
}
HashMap和Hashtable的区别:
共同点:
底层都是哈希算法,都是双列集合
区别:
Collections中的常见方法
Public static void sort (List<?> list) //排序
Public static int binarySearch (List<?> list, T key) //二分查找法(有序集合)
Public static T max (Collection<?> coll) //根据默认排序结果获取集合中的最大值
Public static void reverse (List<?> list) //反转集合
Public static void shuffle(List<?> list) //随机置换
Final、finally、finalize的区别
Final可以修饰类,不能被继承;修饰方法,不能被重写;修饰变量,只能赋值一次
Finally是try语句的一个语句体,不能单独使用,用来释放资源
Finalize是一个方法,当垃圾回收器不存在对该对象的更多引用时,由垃圾回收器调用此方法
如果catch里面有return语句,请问finally的代码还会执行吗?如果会,请问实在return前还是return后
会执行。在return之后
IO流Close方法和flush方法的区别
Close方法:具备刷新功能,在关闭之前,就会刷新一次缓冲区,将缓冲区的字节全部刷新到文件上,再关闭,close方法刷完之后就不能写了
Flush方法:具备刷新功能,刷完之后还能继续写
字节流与字符流。
字节流:file.inputStream;file.outputStream
字符流:file.read;file.write(只能拷贝纯文本,一般不推荐拷贝,需要将字节转为字符再将字符转为字节)
只读或只写的时候用字符流。读取时是按照字符的大小读取的,不会出现半个中文
装饰类设计模式
递归
自己调用自己
递归的弊端:不能调用次数过多,容易导致内存溢出
递归的好处:不用知道循环次数
构造方法不能递归调用
递归调用是否必须有返回值?
不一定(可以有,也可以没有)
Io序列流
序列流可以把多个字节输入流整合成一个,将从被整合的第一个流开始读,读完一个之后继续读第二个,以此类推
序列流整合两个:SequenceInputStream sis = new SequenceInputStream(fis1,fis2)
序列流整合多个:将流对象添加到集合Vector中;创建枚举
IO流(内存输出流)
该输出流可以向内存中写入数据,写出之后可以一次性获取出所有数据
使用方式:1.创建对象:new ByteArrayOutputStream()
2.写出数据:write(int),write(byte[])
3.获取数据:toByteArray()
IO流(对象操作流)
该流可以将一个对象写出,或者读取一个对象到程序中。也就是执行了序列化和反序列化操作。对象需要实现Seralizable(public class person implements Seralizable)
使用方式:new objectOutputStream(outputStream),writeObject
读档:new objectInputStream(),readObject
对象操作流优化:将对象存储在集合中写出
读取:ArrayList list = (ArrayList)ois.readObject();
For(Person p : list){}
IO流(数据输入输出流)
DataIuputStream,DataOutputStream可以按照基本数据类型大小读写数据
例如按Long大小写出一个数字,写出时该数据占8字节,读取的时候也可以按照Long类型读取,一次读取8个字节
使用方式:DataOutputStream(OutputStream),writeInt(),writeLong()
IO流(标准输入输出流)
System.in是InputStream,标准输入流,默认可以从键盘输入读取数据
System.out是PrintStream,标准输出流,默认可以向Console中输出字符和字节数据
Properties的概述和作为Map的作用
Properties类表示了一个持久的属性集
Propertis可保存在流中或从流中加载
属性列表中没个键及其对应值都是一个字符串
Propertis的特殊功能:
Public Object setProperty(String key, String value)
Public String getProperty(String key)
Public Enmerator stringPropertyNames()
Load():prop.load(new FileInputStream(“xxx.properties”));
Store():prop.store(new FileOuputStream(“xxx.properties”),”liebiaomiaoshu”)
多线程
1. 什么是线程
线程是程序执行的一条路径,一个进程可以包含多条线程
多线程并发执行可以提高程序的效率,可以同时完成多项工作
多线程并行与并发的区别
并行就是两个任务同时运行,就是甲任务进行的同时,乙任务也在进行。(需要多核CPU)
并发是值两个任务都请求运行,而处理器只能接受一个任务,就把两个任务安排轮流进行,由于时间间隔较短,使人感觉两个任务都在运行
Java程序运行原理:
Java命令会启动java虚拟机,启动JVM,等于启动了一个应用程序,也就是启动了一个进程,该进程会自动启动一个“主线程”,然后主线程去调用某个类的 main 方法
JVM启动是多线程吗:
JVM启动至少启动了垃圾回收线程和主线程,所以是多线程的
使用多线程
实现方式一
单例设计模式:保证类在内存中只有一个对象
//饿汉式
Class Singleton{
Timer类(计时器)
在指定时间安排指定任务
Timer t = new Timer();
//第一参数时安排的任务,第二个参数是执行的时间,第三个参数是过多长时间再重复执行
t.schedule(new MyTimerTask(), new Date(188, 6, 1, 14, 22, 50), 3000)
同步代码块
线程安全
多线程并发操作同一数据时,就有可能出现线程安全问题
使用同步技术可以解决这种问题,把操作数据的代码进行同步,不要多个线程一起操作
Vector是线程安全的,ArrayList是线程不安全的
StringBuffer是线程安全的,StringBuilder是线程不安全的
Hashtable是线程安全的,HashMap是线程不安全的
死锁
多线程同步的时候,如果同步代码嵌套,使用相同锁,就有可能出现死锁
两个线程间的通信
二叉树的基础遍历
以根节点第几个被访问为根据