java基础 总结版
基础知识
对象和类:
Java中万物皆对象,而类是对一些具有相同属性或者行为的对象的抽象,是一个模板。
创建对象的5种方式:
- 使用new关键字
- 使用Class类的newInstance方法
- 使用Constructor类的newInstance方法
- 使用clone方法
- 使用反序列化
类中含有
成员属性:在类内部,方法体外部,作用于整个类,
成员方法,和成员变量同级
局部变量,在方法体内部,作用于方法体。当成员变量和局部变量重名时,调用的时距离最近的变量。
局部变量就是在类中的方法中定义的变量,在方法中临时保存数据,局部变量存储在内存的栈中,生命周期是随方法的调用而存在,随方法的调用完毕而消失。
成员变量就是在类中定义,用来描述对象将要有什么,成员变量存储在内存的堆中,生命周期是对对象的存在而存在,随对象的消失而消失。
类变量,带有修饰符static的变量,随着属于类,随着类的创建而存在,其实例对象共享一个变量。
构造方法:一般用于初始化对象,当遇到继承的时候要注意关键字this和super,都只能存在于第一行,所以只能有一个!只能被new(实例化对象),不能被调用,
对象则是类的具体的实例,有具体的属性和行为。
基本数据类型:8
Byte(1字节) short(2) int(4) long(8) char(2) boolean float(4) double(8)
类型转换:向上转型(小→大) 向下转型(大→小)
引用数据类型:数组,类,接口,
变量类型:成员变量,局部变量,类变量
访问权限:
Public:所有类可见
Private:同一类内部可见
Protented:对同以包内的类和不同包中的有继承关系的类可见
实例化子类对象,子类不重写protected修饰的变量或方法,可在子类对象中调用父类属性或方法。(其实就是继承了父类,然后自身拥有了和父类相同的属性或方法,然后可以被调用,若此时把父类修饰符改为private,则子类对象中不能调用,就算实例化父类也不行!!!)
Friendly:同一个包内可见。
Synchronized:关于线程安全的时候的同步锁,当临界资源被争抢使用时需要添加,表示用以时间只能被一条线程使用。
Volatile:修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值,而且当成员变量发生变化时,强迫线程将变化值写回到共享内存。
Transient:临时变量
循环结构:do---while while for foreach iterator listiterator
String:
码点:unicode值 代码单元:对应的字符!!!
String:长度不可变
StringBuffer:线程安全,速度慢,支持同步
StringBuilder:线程不安全,速度快,不可同步
关于String,StringBuffer,StringBuilder的区别,
String长度不可变,但是却可以让字符串被共享,只有字符串常量是被共享的,而+或substring等操作产生的结果并不是共享的,所以判断字符串是否相等的话不能用==,要用equals方法,==是判断是否放在了同一个地方。
数组:用来存放同一种类型的容器,由于是同一类型,所以操作时候不需要考虑类型转换问题,效率高!
类的加载:
类的加载指的是将类的.class文件中的二进制数据读入内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象放在堆区中,和方法区中的类就像镜面映射得到的一样。
类的加载是由类加载器完成的,类加载器通常是由JVM来提供的,类加载的过程是父亲委托机制,在这种机制中,先委托父加载器,直到根类加载器,然后再由最初的加载器加载,如果都无法加载则报错ClassNotFound的异常,
然后连接:负责把类的二进制数据合并到jre中,该过程又分为三个阶段:
验证:确保被加载的类的正确性,包括四方面的检查:
- 类文件的检查:确保程序使用的是Java类文件的固定文件
- 语义检查:确保类符合Java语言的语法规定
- 字节码验证:确保字节码可以被jvm安全执行
- 二进制兼容验证:相互调用的类之间的协调一致
准备:jvm为类的静态变量分配内存,并设置默认的初始值,
解析:jvm会把类的二进制数据中的符号引用替换为直接引用
最后是类的初始化:为类的静态变量赋予正确的初始值
所有的jvm的实现,必须在每个类或接口在Java程序“首次主动使用“的才会初始化他们。
主动使用的六种情况:
- 创建类的实例
- 访问某个类或者接口的静态变量,或者对该静态变量赋值
- 调用类的的静态方法
- 反射
- 初始化某个类的子类
- Jvm启动时被标明为启动类的类
自己的理解:当运行Java虚拟机的时候,第一件事就是寻找入口, main方法,test等等,于是加载器开始启动并找出xxx.class文件,,在对它进行加载的过程中,判断是否有基类(有的话继续向上加载...)然后基类中的static被初始化,→导出类的static初始化!然后就执行main方法中的代码!!!
第一部分:面向对象
抽象
对事物进行简化,只提取对问题的解决有帮助的部分。其余的部分全部舍弃。
封装
概念:属性私有化,对外提供公共的get(),set()方法。
举例:创建一个封装类,意思就是该类中是用来提供存放数据的,然后其数据对外是不可修改的(意思是不可以直接通过new对象,然后对象名.属性修改,只能通过提供的公共的set,get方法进行修改,这样的好处就是可以我们可以对类中的属性进行控制,不是任何数据都可以进行存储的。),
继承
子类拥有父类的所有属性和方法,并且还有自己特有的属性和方法。其实现的方式有extends和implements。
所有类的超类都是Object,其中有几个方法需要注意:
Equals():每个类都要有自己equals()方法,当对象内容需要进行比较的时候必须进行重写。
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Equals other = (Equals) obj;
if (address == null) {
if (other.address != null)
return false;
} else if (!address.equals(other.address))
return false;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
hashCode():当重写equlas的时候必须重写hashCode(),因为equals是以hashCode为标准判断是否相等的。
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((address == null) ? 0 : address.hashCode());
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
toString():重写其方法可以直接打印对象名,默认为对象名.toString()
clone():
关键字:extends implements instanceof
继承的设计技巧:
1.将公共操作和域放在超类中
2.不要使用受保护的域(原因1:任何一个人都可以由一个类派生出来一个子类,并编写代码直接访问protected的实例域,从而破坏了封装性!原因2:Java中在同一个包内的所有类都可以访问protected域,而不管它是否为这个类的子类)。
3.使用继承实现“is-a”关系
4.除非所有继承的方法都有意义,否则不要使用继承
5.在覆盖方法时,不要改变预期的行为。
6.使用多态,而非类型信息
7.不要过多的使用反射!反射适用于编写系统程序,通常不适用于编写应用程序。
三大修饰符:
Static:
可修饰方法,变量,代码块,不可以修饰类!
修饰方法时在调用该方法时不需要在实例化所在类,直接类名.方法名调用。随类的加载就被分配内存。可以被继承,没有多态。
修饰变量时该变量代表类变量,该类的所有实例对象都公用一个。
修饰代码块,作用是给类进行初始化!优先于主函数的执行
被static修饰过的就属于类,存在于对象之前!
Static:属于类修饰符,可以把相应的属性或方法变为类级别的,使之伴随类产生和销毁,局部变量不能被修饰为static,静态方法也不能使用非静态变量。
被static 修饰的变量称为静态成员变量或类成员变量。静态成员可以使用类名直接访问,也可以使用对象名访问,推荐直接使用类名访问。
静态成员还是属于成员变量,其生命周期还是随着类的调用而存在,随类的调用结束而消失。
被static修饰的方法成为静态方法,静态方法可以直接调用同类中的静态成员,但是不能直接调用非静态成员,(需要通过创建类的对象,然后通过对象来访问非静态成员),在普通成员方法中可以直接访问同类中的静态成员和非静态成员,静态方法不能直接调用非静态方法,需要通过对象来访问非静态方法。
总结:static 只能修饰变量和方法,不能修饰类!!!
修饰过的方法和变量可以在类中任何地方直接调用,没修饰的只能先实例化在调用。
代码块:静态代码块→初始化类
动态代码块→初始化(所有)对象
构造函数→初始化(特定)对象
Abstract:
可修饰类,方法,修饰方法的时候不需要因为是抽象方法没有方法体,所以就不要写{},直接+;。
修饰类的时候此类不可被实例化,只能被子类实现其抽象方法后通过实例化子类调用其属性方法,
修饰的方法为抽象方法,只有声明没有实现,只能定义在抽象类中。
修饰类:抽象类,含有抽象方法的类一定是抽象类,抽象类中的抽象方法一定要被子类继承,若子类没有继承,则子类也一定为抽象类。可以由构造方法!!
方法:没有具体的方法体,只是一个抽象概念,需要被重写使用,只有声明没有实现。
Final:可修饰类,方法,变量。
修饰类不能被继承,
修饰方法可以被继承,不可以被重写!
修饰变量时表示该变量不可被修改,声明的同时进行初始化!局部变量可以先声明再初始化,但是在使用该变量之前一定要初始化!
抽象类:
如果一个类没有足够的信息来描述一个具体的对象,而需要其他具体的类来支撑它,我们称它为抽象类,如果一个类包含一个或多个抽象方法,该类必须被限定为抽象的,如果从一个抽象类继承,并想创建它的对象,那么必须为基类的所有的抽象方法提供方法定义,否则子类也还是抽象类,还必须强制添加abstract。抽象类不能被实例化对象,就算类内部没有抽象方法,只要被定义为abstract,就不能被实例化!子类的抽象方法不能和父类抽象方法同名。
接口:
接口使得抽象类的概念更加上升了一步,更加纯粹,也不能被实例化,是对类的一组需求描述,这些类要遵从接口描述的统一格式进行定义,和类的定义基本相同,接口没有实例域,关键字改为interface,接口的方法自动为public abstract,实例变量也是static public,没有构造方法。在实现接口的时候,必须把方法声明为Public,
比如:要使用Arrays.sort()方法对一个类对象数组进行排序,则那个类一定要实现Comparable接口,接口中可以定义静态方法,一般是在实现自己定义的接口的时候可以定义静态方法,可以省略创建一个实现类的麻烦。还可以定义默认方法。
思考:你想使用我提供的方法,那么你就要按照我的标准去提供我所需要的资源,这样我才让你用我的东西!!!
接口的作用?(如何实现降低耦合)
降低耦合的意思是由串行开发变为并行开发,之前的一个个类之间相互实例化调用彼此的方法,现在都改为接口,接口有之前类的抽象方法,之前类变为接口的实现类,这样各个类之间就没有了必然的关系,不存在谁调用谁的问题,都是调用接口,再由类去实现接口,大大降低了这个类错了,其他的类也要改的麻烦!
接口与回调:
回调是一种程序设计模式,在这种模式中,可以指出某个特定事件发生时应该采取的动作。
抽象类与接口的区别:
抽象类可以含有抽象方法和非抽象方法,有构造函数,接口中只能有抽象方法,没有构造函数。抽象类属于类,只能单继承,而接口可以多继承,抽象类比较笨重,而接口很灵活,很方便。但是有限选择类,设计时候用就接口,但是可以实现其工具类。两者的抽象层次不同,抽象类是对类抽象(整体),而接口是对行为的抽象(局部)。
重写:
又叫覆盖,一般用于继承或者实现接口关系中,从父类继承过来的方法不再适用于子类,子类需要对其进行重新定义,
重写的方法名,返回值类型,参数一定要相同。 修饰符相同或者更宽。
静态只能覆盖静态,父类的返回类型若为基本类型时,其子类重写时不能改变返回值类型,若为其他类型,子类重写的方法其返回类型可以是子类。
重载
一个类的内部进行的多态,通过相同的方法名,不同的参数名(个数,类型,顺序)。与返回值类型无关
关于重写和重载:两者是重写是继承多态相关的,子类继承父类的方法,但是不适用自己的情况,于是对其进行重写,调用时,父类引用指向子类对象,调用的是相应子类对象的方法,重载就是一个类内部的事情,优点是屏蔽使用差异,共同的方法名,但是参数不同,调用该方法时,系统会通过匹配所有的该方法名方法,找到具有相同参数列表的方法,调用它。
多态
概括:父类引用→→→子类对象
前提是继承,没有继承就没有多态,多态就是在父类的方法的基础上,子类继承了父类的方法并重写了它,在方法中可以以父类为形参,具体调用方法的时候依据对象的不同,在选择相应的方法,最后的效果就是 从同一个父类中导出的子类调用同一个父类的方法,可以产生不同的结果。
This和super:
- 只能在方法内部使用,表示对“调用方法的那个对象“的引用,如果是在方法内调用同一个类的另一个方法,则不需要使用this,当出现this时,可以替换为此处所处的类实例出来的一个对象,可以使用在构造函数中,构造函数的重载中使用this()可指代其他的构造方法(依据参数),但是构造方法中的this必须在第一行!!!
Super和this的使用差不多,只不过他是指向this的父类对象,注意子类的构造方法第一行自动添加super(),
内部类:
分为局部内部类,成员内部类,静态内部类,匿名内部类
依据内部类在外部类中的位置所判定的。主要是匿名内部类,其实主要就是抽象类和接口的实现,省略的是实现类的创建,但是若所创建的对象之后你还要使用的话不要使用匿名内部类,可使用局部匿名类,对象可以多次使用。
代理:
第二部分:集合
整体概括:
集合框架:是一个用来代表和操纵集合的统一架构,所有的集合框架都包含 接口,实现类,算法
框架:↓↓↓(接口)
(类):
需满足:该框架必须是高性能的,基本集合(动态数组,链表,树,哈希表)的实现也必须是高性能的
该框架允许不同类型的集合,以类似的方式工作,具有高度的互操作性,
对一个集合的扩展和适应必须是简单的。
容器:分为collection和map
Collection又分为list和set
Map和collection平级,
List
有序的collection,此接口的用户可以对列表中每个元素的插入位置进行精准的控制,用户可以根据元素的整数索引访问元素,并搜索列表中的元素。列表通常允许重复的元素,listiterator只适用于List,其他的Set,Map并没有这样的方法。
Arraylist:以数组的形式实现,查询快,插入删除慢,不是线程同步的
原因:数组是一块连续的内存空间
Linkedlist:以链表的形式实现,插入删除快,查询慢,不是线程同步的
原因:链表是一个个独立的空间,将每个对象存放在独立的结点中,每个结点还存放着序列中下一个结点的引用。在java程序设计中,所有的链表都是双向链接的(前驱和后继)。
Vector:可以实现可增长的对象数组,大小可以根据需要增大或缩小,是同步的。
将List转化为一个int[ ]数组:
Int [ ] array = new int [ list.size()];
For(int I = 0;i<list.size();i++){
Array[i] = list.get[i];
}
将一个数组转化为list集合:
List<intrger> list = new ArrayList<integer>();
For(int I : array){
List.add(i);
}
Set
一个不包含重复元素的collection,,禁止将自身作为作为元素。
HashSet:实际上就是HashMap的一个实例,详见hashmap。线程不同步
判断hashCode是否相同,不同就存储,相同再判断equals方法,都相同了不存。
TreeSet:基于TreeMap的NavigableSet实现,使用元素的自然顺序对元素进行排序,线程不同步,底层时二叉树进行的排序。保证元素唯一性的方式就是参考实现接口的compare()方法返回的结果是否为0,为0则不存储。如果没有实现comparator接口则无法进行比较功能,运行时会发生classCastException异常。1.元素自身具备比较功能:元素就要实现Comparable接口,覆盖compare to()方法。2.集合自身具备比较功能:定义一个类实现Comparator接口,覆盖compare()方法,将该类对象作为参数传递给TreeSet集合的构造函数。
LinkedHashSet:有序,HashSet的子类。
TreeSet以集合自身具备比较功能:
代码:
class Person1{
String name;
int age;
public Person1(String name,int age) {
// TODO Auto-generated constructor stub
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person1 [name=" + name + ", age=" + age + "]";
}
}
class ComparatorByName implements Comparator{
public int compare(Object o1,Object o2){
Person1 p1 = (Person1) o1;
Person1 p2 = (Person1) o2;
int temp = 0;
temp = p1.name.compareTo(p2.name);
return temp==0?p1.age-p2.age:temp;
}
}
public class TreeSetDemo2 {
public static void main(String[] args) {
TreeSet ts = new TreeSet(new ComparatorByName());
ts.add(new Person1("lily",15));
ts.add(new Person1("xily",12));
ts.add(new Person1("nily",15));
ts.add(new Person1("sily",22));
Iterator it = ts.iterator();
while (it.hasNext()) {
Person1 p = (Person1)it.next();
System.out.println(p);
}
}
}
TreeSet让元素自身具备比较功能:
代码:
class Person implements Comparable{
public String name;
public int age;
public Person(){}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
/*public int hashCode() {
return name.hashCode()+age*31;
}
public boolean equals(Object obj) {
if (obj == this) return true;
if (obj==null) return false;
if (this.getClass()!=obj.getClass()) return false;
Person other = (Person)obj;
if(other.name.equals(name)&&other.age==age) return true;
return false;
}*/
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
/*@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}*/
@Override
public int compareTo(Object o) {
Person p = (Person) o;
int temp = this.age - p.age;
return temp == 0 ?this.name.compareTo(p.name):temp;
}
}
public class TreeSetDemo {
public static void main(String[] args) {
TreeSet ts = new TreeSet();
ts.add(new Person("lily",28));
ts.add(new Person("lily",15));
ts.add(new Person("lily",36));
Iterator iterator = ts.iterator();
while (iterator.hasNext()) {
Person p = (Person)iterator.next();
System.out.println(p);
}
}
}
Map
Map:一次添加一对元素,Collection一次添加一个元素。
Map中存储的就是键值对,map必须保证键的唯一。当set已经存在的键信息时,会替换掉原有的信息。
遍历map的三种方式:
//one
//Map.Entry:是一个单独的接口,java.util.Interface Map.Entry<K,V>
//将map中的内容进行遍历
/*
* entry function:
* equals(),getClass(),getValue(),getKey(),setValue(),toString()...
*/
for (Map.Entry<String, String> entry : map.entrySet()) {
if (entry.getValue().equalsIgnoreCase("Jsp")) {
System.out.println("name:"+entry.getKey());
}
}
//two
/*
* use map.keySet() function return (a set view of the keys contained in this map)
* pass get "key" to use map.get(key) to get value;
*/
Set<String> keySet = map.keySet();
for (String key : keySet) {
if (key.equalsIgnoreCase("lucy")) {
System.out.println(map.get(key));
}
}
//three
/*
* use iterator
* Iterator:java.util.Interface Iterator<E>
* attr: E: iterator return type
* function: hasNext()→if iterator has more element return true
* next()→return next element
* remove()→delete this iterator return last element
*/
//效率高!!!★★★
Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, String> entry = it.next();
String value = entry.getValue();
if (value.equalsIgnoreCase("oracle")) {
System.out.println(entry.getKey());
}
}
Hashtable:
继承Dictionary类(此类已过时),实现map接口,
Put方法: 1. 判断value是否为空,为空直接抛出异常。
2.计算key的hash值,获得在数组中的位置,位置上不为空则迭代,遇到相同的直接替换,否则,插入到相应位置。
遍历:1.table.keys() 2.elements() 3.keyset() 4.entrySet()
Hashmap和hashtable的比较:
1.继承关系 2.是否允许为空 3.是否同步
集合的遍历:
For循环,foreach循环,和iterator,其中for适用于所有情况,foreach只适用于collection, iterator适用于所有情况,但是listiterator只适用于list。遍历数组型集合的时候尽可能使用整数索引的方法来访问,而遍历链表型集合的时候尽可能的使用iterator迭代器来访问。
Foreach()循环时不能对元素进行赋值
★★★可以将iterator.next和InputStream.read看作为等效的。
最有效移除集合元素的方式:iterator.remove()方法。
有关集合的技巧:
看到Array就是数组结构,有角标,查询速度很快。
看到Link就是链表结构,增删速度快,而且有特有方法。
看到hash就是哈希表,想到哈希值,唯一性,存入该结构的元素必须覆盖hashCode,equals。
看到tree就是二叉树,排序,就要对元素或集合实现comparator或comparable接口。
Collections工具类:
addAll (boolean) |
将所有指定元素添加到collection中 |
binarySearch (int) |
使用二分搜索法获得指定对象 |
Copy (void) |
将所有元素从一个列表复制到另一个列表 |
Emptylist( List) |
返回一个空的List |
emptySet |
|
emptyMap |
|
Fill (void) |
使用指定元素替换所有元素 |
Frequency (int) |
返回collection中等于指定对象的元素数 |
IndexOfSubList (int) |
第一次出现目标的起始位置 |
replaceAll (boolean) |
替换列表中出现的所有某一值 |
Reverse (void) |
反转列表中元素的顺序 |
Sort (void) |
根据自然顺序对列表中的元素进行升序排序 |
SynchronizedCollection |
返回指定collection支持的同步 |
|
|
|
|
将数组转化为List集合。Arrays.asList()
集合转化为数组:Collection.toArray()
第三部分:I/O
流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或者抽象。IO流用来处理设备之间的数据传输。
分类:
按方向→→→→→→→→输入流,输出流
按操作的数据类型→→→字节流,字符流
字符流=字节流+编码表。
读取一个文本文件:
/*第一种:
* try {
FileReader fr = new FileReader("D:\\cc.txt");
int ch = 0;
while ((ch = fr.read())!=-1) {
System.out.print((char)ch);
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}*/
/*
* 第二种:
FileReader fr = null;
try {
fr = new FileReader("D:\\cc.txt");
char[] ch = new char[2];
int leng = 0;
while ((leng=fr.read(ch))!=-1) {
System.out.print(new String(ch));
}
} catch (FileNotFoundException e) {
}finally {
fr.close();
}
*/
字符流缓冲区:
缓冲区的出现是为了提高对数据的读写效率,缓冲区要结合流才可以使用。在流的基础上对流的功能进行了增强。
缓冲区的使用:
/*
* 写入
FileWriter fw = new FileWriter("D:\\cc.txt");
BufferedWriter bw = new BufferedWriter(fw);
for (int i = 0; i < 5; i++) {
bw.write("莉莉:"+i);
bw.newLine();
}
bw.close();
fw.close();
//读取
FileReader fr = new FileReader("D:\\cc.txt");
BufferedReader br = new BufferedReader(fr);
String line = null;
while ((line=br.readLine())!=null) {
System.out.println(line);
}
br.close();
fr.close();
}
*/
字节流及缓冲:
public static void copy_1() throws IOException{
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream("D:\\cc.txt");
fos = new FileOutputStream("F:\\lily.txt");
byte[] by = new byte[10];
int leng = 0;
while ((leng=fis.read(by))!=-1) {
fos.write(by);
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
fos.close();
fis.close();
}
}
public static void copy_2() throws IOException{
FileInputStream fis = null;
BufferedInputStream bis = null;
FileOutputStream fos = null;
BufferedOutputStream bos = null;
try {
fis = new FileInputStream("D:\\cc.txt");
bis = new BufferedInputStream(fis);
fos = new FileOutputStream("D:\\dd.txt");
bos = new BufferedOutputStream(fos);
byte[] by = new byte[10];
int leng = 0;
while ((leng=bis.read(by))!=-1) {
bos.write(by);
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
bos.close();
bis.close();
}
}
对象序列化:
ObjectOutputStream()
ObjectInputStream()
1 增强了缓冲作用
2 可以操作8种基本类型
3 增强了读写对象的功能
write Object(Object obj); 将对象写入文件
Object readObject(); 从文件读取对象
对象在流上传输的过程叫做对象序列化
要求:参与序列化的对象必须实现一个接口:
java.io.Serializable
文件到达尾部的标志:抛出异常:java.io.EOFException
若参与对象序列化的对象的属性也是对象类型,此对象必须也是可序列化的
若集合中存储的元素是对象类型,集合参与对象序列化时,储存的元素也必须时刻序列化的
transient 关键字表示该条属性不参与序列化
private static final long serialVersionUID = 1L;是序列化对象的标识,如果序列化对象
在反序列化时添加了属性或者方法,却并没有加入序列id那么会报错
转换流:
字符流和字节流之间的桥梁,方便了字符流和字节流之间的操作。
InoutStreamReader:字节到字符的桥梁
OutputStreamWriter:字符到字节的桥梁
关于流的操作规律,通过四个明确即可:
- 明确源和目的:InputStream和OutputStream
- 明确是否是文本数据源:Reader,Writer和InputStream,OutputStream
- 明确具体的设备:File,System.out,数组,Socket流和File,System.out,数组,Socket流
- 是否需要其他额外功能,缓冲区
File类:
用来将文件或者文件成对象,方便对文件与文件夹的属性信息进行操作。File对象可以作为参数传递给流的构造函数。
第四部分:线程
概念:
多线程:指的是这个程序(一个进程)运行时产生了不止一个线程。
并行:多个cpu实例或者多台机器同时执行一段处理逻辑,是真正的同时。
并发:通过cpu调度算法,让用户看上去以为是同时在执行多个任务。
进程和线程一样都分为五个阶段:创建,就绪,运行,阻塞,终止
Thread
Thread线程中的对象是分别独立进行任务的,每个都是一个进程。数据是独立的。
继承Thread类,覆盖run()方法
class MyThread extends Thread {
public void run(){}
}
创建线程对象:
MyThread mt = new MyThread();
调用start()方法开启线程
mt.start();
Runnable
可以理解为一个进程下的多个线程!数据共享
实现Runnable接口,覆盖run()方法
class MyThread implements Runnable(){
public void run(){}
}
创建目标对象
MyThread mt = new MyThread();
创建线程对象
Thread t = new Thread(mt);
调用start()方法开启线程
t.start();
线程池
线程池的顶级接口是Executor,但是真正的线程池接口是ExecutorService,因为严格意义上讲Executor并不是一个线程池。
作用:限制系统中执行线程的数量,根据系统的环境情况,可以手动或自动设置线程数量达到运行的最佳效果。还可以减少创建和销毁线程的次数,每个工作线程都可以被重复利用可执行多个任务。
常用的线程池:
- newSingleThreadExecutor:创建一个单线程的线程池。
- newFixedThreadPool:创建一个固定大小的线程池
- newCachedThreadPool:创建一个可缓存的线程池如果线程池的大小,超过了处理任务所需要的线程,那么就会回收部分空闲(60s不执行任务)的线程。不够的话会创建新的线程出来。
- newScheduledThreadPool:创建一个大小无限的线程池,此线程池支持定时以及周期性执行任务的需求。
Callable
Futrue
线程的状态:
初始状态-->就绪状态-->可运行状态-->运行状态-->终止状态
初始状态:创建线程对象后,但在调用start方法前的状态
就绪状态:调用start方法之后但未被CPU分配资源
可运行:
线程同步:
1 临界资源:被多个线程共享的对象
2 原子操作:多步不可分割的操作,其顺序和步骤不可被打破。
3 线程同步:多线程并发时,为了保证临界资源的正确性,
而不能破坏程序中的原子操作。
几个方法:
sleep(long ms) 让当前线程处于休眠状态,释放cpu但是不会释放锁标记
join();加入到自身线程中,让其他线程优先执行。
wait(); 此方法必须使用在该对象的同步代码块中;
会让当前线程处于无限期的等待状态
会让当前线程释放所拥有的锁标记,同时释放cpu
notify();通知一个线程从等待中结束
notiyAll();通知所有县城从等待中结束
必须使用在该对象的同步代码块zhong
通知线程从等待中结束
不会让当前线程释放锁标记
第五部分:异常
异常:就是程序在运行时出现不正常情况
Throwable:Throwable 类是 Java 语言中所有错误或异常的超类。只有当对象是此类(或其子类之一)的实例时,才能通过 Java 虚拟机或者 Java throw 语句抛出。类似地,只有此类或其子类之一才可以是 catch 子句中的参数类型。
Error:描述了Java运行时系统的内部错误和资源耗尽错误,应用程序不应该抛出这种错误,如果出现了这种错误,除了通报给用户,并尽力使程序安全的终止之外,再也无能为力了。
Exception:
Runtimeexception:由程序错误而导致的异常
- 错误的类型转换
- 数组访问越界
- 访问null指针
其他异常:程序没错,其他情况发生的异常。如io…
- 试图在文件尾部后面读取数据
- 试图打开一个不存在的文件
- 试图根据给定的字符串查找Class对象,而这个字符串表示的类型并不存在
Java将error类和runtimeException类的所有异常称为非受查异常(不用声明,系统错误处理不了,逻辑错误有声明的时间不如修改一下逻辑错误),所有其他的异常称为受查异常。
抛出异常:throw和throws
当在代码中遇到可能出现的错误情况时就要给其考虑在内,若错误后果不可恢复或者回复很麻烦的情况下,可以通过throw new 异常类()将其抛出,说明此处有错误需要处理,然后通过throws关键字的解决方案解决之后重新返回此处或者进行其他执行。
抛出异常的四种情况:throws声明异常
- 调用一个抛出受查异常的方法,例如:FileInputStream构造器。
- 程序运行过程中发现错误,并且利用throw语句抛出一个受查异常。
- 程序出现错误
- Java虚拟机运行时出现内部错误
关于继承中的异常:
如果子类覆盖了父类的一个方法,子类方法中声明的受查异常不能比父类方法中声明的异常更通用(子类可以抛出更特定的异常或者没有异常)。相反如果父类没有抛出任何异常,子类也不能。
异常的捕获:
Try():用来捕获异常,在这个块中尝试可能产生异常的各种方法调用
Catch():在此处处理捕获的各种异常,而且针对每个要捕获的异常准备相应的处理程序。
Finally():
使用异常机制的技巧:
- 异常处理不能代替简单的测试
- 不要过分的细化异常
- 利用异常层次结构
- 不要压制异常
- 在检测错误时,“苛刻”要比放任好
- 不要羞于传递异常
对于异常的处理:终止与恢复
- 终止模型:在此模型中将假设错误非常关键,以至于程序无法异常产生的地方继续执行,一旦异常被抛出,就表明错误无法挽回,也不能回来继续执行
- 恢复模型:异常处理程序的工作是修正错误,然后重新尝试调用出问题的方法,并认为第二次能成功。这种遇到错误时不能抛出,而是调用方法来修正该错误,或者把try()块放在while循环中,直到得到满意的结果。这种方法并不常用,应为会导致耦合。
Error和exception的区别:
Error属于编译时错误,根本不会编译通过,也就是不会生成.class文件,exception属于运行时错误,只有在调用的时候才会报错,比如空指针异常,数组下标越界…
自定义异常:
日志:
断言:
相关面试题:
- 一个“.java”源文件中是否可以包括多个类(不是内部类),有什么限制?
可以有很多类,但是只能有一个public类,源文件名与之相同,不过最好一个“.java”文件只包含一个类。
- &&和&的区别?
&&与&是都逻辑与,只有符号两边的表达式都为true时整个式子才为true。
&&另外还具有短路作用,当第一个式子为false的时候此时系统直接判定整个式子为false输出,后面表达式不再判断,可以提高系统效率。
&另外有按位与功能,是二进制之间的运算,按位一个一个与。
- Java如何跳出当前的多重其嵌套循环?
使用带标签的break或continue。