小白和老手都应该看看的总结
输入
java.util.Scanner 是 Java5 的新特征,我们可以通过 Scanner 类来获取用户的输入。
下面是创建 Scanner 对象的基本语法:
Scanner s = new Scanner(System.in);
使用方法如下:
//对应类型用对应的方法接收 String stringx=s.next(); String stringy=s.nextLine(); int
intx=s.nextInt(); long longx=s.nextLong(); short shortx=s.nextShort(); float
floatx=s.nextFloat(); double doublex=s.nextDouble(); BigInteger
bigInteger=s.nextBigInteger(); BigDecimal bigDecimal=s.nextBigDecimal();
注意:
1、next和nextLine的区别:next读取到空格停止,nextLine读取到回车停止,读取到空格不会停止。
2、nextInt不要和nextLine混用,如果nextLine在nextInt后面使用,会有吸收掉了本行的换行符而并没有接收到下一行数据的问题
输出
System是java.lang里面的一个类
out是System的静态数据成员,而且这个成员是java.io.PrintStream类的引用
println()和print()就是java.io.PrintStream类里的方法.
被关键字static修饰的成员可以直接通过"类名.成员名"来引用,无需创建类的实例。所以System.out是调用了System类的静态数据成员out。
第一种:
System.out.println();
是最常用的输出语句,它会把括号里的内容转换成字符串输出到控制台,并且结尾换行。
1)输出的是一个基本数据类型,会自动转换成字符串,
2)输出的是一个对象,会自动调用对象的toString()方法
第二种:
System.out.print();
和第一种一样,只是结尾不换行。
第三种:
System.out.printf();
这个方法延续了C语言的输出方式,通过格式化文本和参数列表输出比如:
八种基本类型
基本数据类型的变量是存储在栈内存中,而引用类型变量存储在栈内存中,保存的是实际对象在堆内存中的地址。
注意:有两个大数类:BigInteger,BigDecimal分别是整数和小数
自动装箱: java自动将原始类型转化为引用类型的过程,编译器调用valueOf方法将原始类型转化为对象类型。
自动拆箱: java自动将引用类型转化为原始类型的过程,编译器调用intValue(),doubleValue()这类方法将对象转换成原始类型值
例子:
Integer a = 3; //自动装箱 int b = a; //自动拆箱
条件分支
1)if语句中必须是一个布尔值,而不能是其他类型,这是java特殊的地方,比如判断x是否为null不能写if(!x)而要写if(x==null)
2)switch 语句中的变量类型可以是: byte、short、int 、char。
从 Java SE 7 开始,switch 支持字符串 String 类型了
Switch语句和if else语句的区别
switch case会生成一个跳转表来指示实际的case分支的地址,而if...else是需要遍历条件分支直到命中条件
1)if-else语句更适合于对区间(范围)的判断,而switch语句更适合于对离散值的判断
2)所有的switch都可以用if-else语句替换(if-else对每个离散值分别做判断即可)
但是不是所有的if-else都可以用switch替换(因为区间里值的个数是无限的)
3)当分支较多时,用switch的效率是很高的。因为switch是确定了选择值之后直接跳转到那个特定的分支
switch...case占用较多的空间,因为它要生成跳表,特别是当case常量分布范围很大但实际有效值又比较少,空间利用率很低。
循环流程
for( 初始化; 终止条件; 更新 ) {
}
---------------------------------------------------------------
for(类型 变量名:数组或集合){
}//注意举例:
for(int i:arr){
i=1;
}//arr数组内的值不会被改变
---------------------------------------------------------------
while(进入循环条件){
}
---------------------------------------------------------------
do{
}while(条件)
---------------------------------------------------------------
数组
静态初始化
正确示例:
int ids[]或int[] ids={ 1,2,3,4,5,6,7,8};
错误示例:
int num2[3] = {1,2,3}; // 编译错误,不能在[ ]中指定数组长度
int[] num3;System.out.println(num3.length); // 编译错误,未初始化不能使用
动态初始化
正确示例:
int series[ ]= new int[4];
二维数组:
int arr[ ] [ ] = { {1,2},{3,4},{5,6}};
long[ ][ ] arr = new long[5][5];
arrays类:
java.util.Arrays类能方便地操作数组,它提供的所有方法都是静态的
常用方法:
–copyOf 实现数组的复制 copyOf(int[] a,int
newlength);
–fill 实现数组元素的初始化 fill(int[] a,int val);
–sort 实现数组的排序 sort(int[] a);
–binarySearch 实现排序后的数组元素查找 binarySearch(int[] a,int key);
面向对象的三大核心特性
封装
保证了程序和数据不受外部干扰,不被误用。封装的目的在于保护信息,使用它的主要优点如下。
* 保护信息(阻止外部随意访问内部代码和数据)
* 隐藏细节(一些不需要程序员修改和使用的信息,用户只需要知道怎么用就可以,不需要知道内部如何运行)
* 有利于松耦合,提高系统的独立性。(当一个系统的实现方式发生变化时,只要它的接口不变,就不会影响其他系统的使用)
* 提高软件的复用率,降低成本
继承
继承可以使得子类具有父类的各种属性和方法,而不需要再次编写相同的代码,缺点:提高了类之间的耦合性
继承的语法:修饰符 class 子类名 extends 父类名
1)子类拥有父类非private的属性,方法,子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。子类可以用自己的方式实现父类的方法(重写)。
2)Java的继承是一个子类只能继承一个父类,但是可以多重继承:例如A类继承B类,B类继承C类,这是java继承区别于C++继承的一个特性。
多态
•在面向对象理论中,多态性的定义是:同一操作作用于不同的类的对象,将产生不同的执行结果 。
比如动物类可以指向的猫或狗,他们的“叫”方法就是不一样的,“喵喵”和“汪汪”
好处:多态除了代码的复用性外,还可以降低耦合、可替换性、可扩充性
多态的转型分为向上转型和向下转型两种
向上转型:多态本身就是向上转型过的过程
父类类型 变量名=new 子类类型(); 比如:Set<Integer> set=new HashSet<Integer>();
向下转型:一个子类对象可以使用强制类型转换,将父类引用类型转为子类引用各类型
子类类型 变量名=(子类类型) 父类类型的变量;
开发中实现多态?
接口实现、抽象类、继承父类进行方法重写、同一个类中进行方法重载。
关键字
final 关键字:声明类可以把类定义为不能继承的,比如String
super关键字:用于引用父类中的属性和方法,super.属性、super.方法()
this关键字:用于引用本类中的属性和方法,this.属性、this.方法()
权限关键字
抽象类
在继承关系中,有时基类本身生成对象是不合情理的。
例如,动物作为一个基类可以派生出猫、狗等子类,但动物类本身生成对象明显不合常理。
abstract修饰的类称为抽象类。抽象类的特点:
不能实例化对象、类中可以定义抽象方法、抽象类中可以没有抽象方法。
•abstract修饰的方法称为抽象方法,抽象方法只有声明没有实现,即没有方法体。包含抽象方法的类本身必须被声明为抽象的。
abstract class Animal { private String color ; public abstract void shout(); }
•派生类继承抽象类必须实现抽象类中所有的抽象方法,否则派生类也必须定义为抽象类。
接口
Java中的接口是一系列方法的声明,可以看做是特殊的抽象类,包含常量和方法的声明,而没有变量和方法的实现。
接口的意义:
•弥补Java中单继承机制的不足。
•接口只有方法的定义没有方法的实现,即都是抽象方法,这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)。
接口的定义语法:
interface 接口名称 { 常量 抽象方法 }
类可以通过实现接口的方式来具有接口中定义的功能,基本语法:
–class 类名 implements 接口名 {
–}
–一个类可以同时实现多个接口;
–一个接口可以被多个无关的类实现;
–一个类实现接口必须实现接口中所有的抽象方法,否则必须定义为抽象类。
接口继承:
Java中接口可以继承接口,与类的继承概念一致,
会继承父接口中定义的所有方法和属性。
一个接口可以同时继承多个接口。
接口和抽象类对比
参数抽象类接口
默认的方法实现可以有默认的方法实现接口完全是抽象的。不存在方法的实现
实现子类使用extends关键字来继承抽象类。如果子类不是抽象类的话,它需要提供抽象类中所有声明的方法的实现。子类使用关键字implements
来实现接口。它需要提供接口中所有声明的方法的实现
构造器抽象类可以有构造器接口不能有构造器
main方法抽象方法可以有main方法并且我们可以运行它接口没有main方法,因此我们不能运行它。
多继承抽象方法可以继承一个类和实现多个接口接口只可以继承一个或多个其它接口
速度它比接口速度要快接口稍微慢,因为需要去寻找在类中实现的方法。
添加新方法如果你往抽象类中添加新的方法,你可以给它提供默认的实现。因此你不需要改变你现在的代码。如果你往接口中添加方法,那么你必须改变实现该接口的类。
异常
Java5个异常
ArrayIndexOutOfBoundsExceptions:数组下标越界
NullPointerException:访问null的对象的方法或属性时
ClassCastException:类型转换失败时
ConcurrentModificationException:并发修改异常
ArithmeticException:除零异常
处理异常:
try catch :
try catch:自己处理异常 try { 可能出现异常的代码 } catch(异常类名A e){ 如果出现了异常类A类型的异常,那么执行该代码 }
...(catch可以有多个) finally { 最终肯定必须要执行的代码(例如释放资源的代码) }
注意:finally不一定执行:前面代码遇到如:System.exit(0); return 0;,就不会执行接下来的语句,包括finally。
抛出:
一个方法不处理它产生的异常,而是沿着调用层次向上传递,由调用它的方法来处理这些异常,叫抛出异常。
throws:使用throws关键字,用来方法可能抛出异常的声明。
例如:public void doA(int a) throws Exception1,Exception3{......}
throw:使用throws关键字,用来抛出异常
语法:throw (异常对象);
如:throw new ArithmeticException();
注意事项:
•避免过大的try块,不要把不会出现异常的代码放到try块里面,尽量保持一个try块对应一个或多个异常。
•细化异常的类型,不要不管什么类型的异常都写成Excetpion。
•不要把自己能处理的异常抛给别人。
自定义异常
•如果JDK提供的异常类型不能满足需求的时候,程序员可以自定义一些异常类来描述自身程序中的异常信息。
•程序员自定义异常必须是Throwable的直接或间接子类。
•在程序中获得异常信息一般会调用异常对象的getMessage,printStackTrace,toString方法,所以自定义异常一般会重写以上三个方法。
public class SpecialException extends Exception { @Override public String
getMessage() {return "message";} @Override public void printStackTrace()
{System.out.println(message);} @Override public String toString() {return
"message";} }
字符串
String
String是字符串final常量类,值一经赋值,其值不可变(指的是所指向的内存值不可修改,但可以改变指向),而且无法被继承。
初始化
•String name= new String(“小猫”);
和创建对象过程没有区别,创建一个新的String对象,并用name指向它。
•String sex = “女”;
过程:
* 先在常量池中查找“女”,如果没有则创建对象
* 在栈中创建引用sex,
* 将sex指向对象“女” String s1 = "abc"; //"abc"是一个对象 String s2 = new String("abc");
//这里变成两个对象,在内存中存在两个,包括对象“abc” 和 new 出来的对象 String s3 = "abc";
//这里的‘abc’ַ和s1的‘abc’是同一个对象,二者的内存地址一样。 System.out.println(s1==s2);//false
System.out.println(s1==s3);//true
部分api
字符串连接concat(String str)、“+”运算符 字符串查找indexOf (String str)、lastIndexOf(String
str)、charAt(int indexOf) 字符串分割split(String regex) 字符串比较compareTo(String str):
忽略大小写equalslgnoreCase(String str) 变成字符数组toCharArray()
StringBuild和 StringBuffer
因为String的值是不可变的,每次对String的操作都会生成新的String对象,这样效率低下,大量浪费空间。
第一行:我们先栈里起名字叫cat,在堆里开辟空间,存入“小猫”,并用cat指向它
第二行:我们新开辟了一个空间,存入“小猫world”,并用cat指向它。
这个过程显然又费时又费力。
为了应对字符串相关的操作,谷歌引入了两个新的类:StringBuffer类和StringBuild类,它们能够被多次的修改,并且不产生新的未使用对象。
StringBuild和 StringBuffer 之间的最大不同在于 StringBuilder
的方法不是线程安全的(不能同步访问),StringBuffer是线程安全的。(StringBuffer 中的方法大都采用了 synchronized
关键字进行修饰)
由于 StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类。
在应用程序要求线程安全的情况下,则必须使用 StringBuffer 类。
集合
map/set
每一种set有对应类型的map
HashSet(map)无序,底层哈希表+链表(1.8后,数量大于8后链表改为红黑树优化性能),增删改查O(1)
TreeSet(map)有序(人规定排序的规则),底层是红黑树,但是增删改查O(logN)
LinkerHashSet(map)添加一个双向链表维护了插入的顺序。
一般情况用HashSet(map)因为效率高
list
ArrayList,底层是数组,查询快,增删慢。
线程不安全,速度快
Vector, 底层是数组,查询快,增删慢。
线程安全,速度慢
LinkedList,底层数据结构是链表,查询慢,增删快。
线程不安全,速度快
PriorityQueue优先队列,底层是堆。增删改查O(logN)
API
ArrayList<Integer> list=new ArrayList<Integer>(); 增: add(E e):将指定的元素添加到此列表的尾部
add(int index, E element):将指定的元素插入此列表中的指定位置 删: remove(int index):移除此列表中指定位置上的元素
改: set(int index, E element):用element替换index上的数 查: get(int index):返回下标index上的元素
size():返回此列表中的元素数 //map Map<Integer, Integer> map=new HashMap<Integer,
Integer>(); int size()//K-V关系数量 boolean isEmpty()//是否为空 增: V
put(K key,V value)//放入K-V键值对 void putAll(Map<K,V> m)//放入m包含的所以键值对 删: V
remove(Object key)//删除key对应的键值对 void clear()//删除所有键值对 改: 直接put,会覆盖旧的记录 查:
boolean containsKey(Object key)//是否包含key boolean
containsValue(Object value)//是否包含value V get(Object key)//得到key对应的value 生成集合:
Set<K> keySet()//返回包含所有key的set Collection<V> values()//返回包含所有value的Collection
TreeMap特有: public K firstKey()//返回第一个key(最高) public K lastKey()//返回最后一个key(最低)
//set,大部分和map类似 增:add 删:remove 查:contains
Iterator
主要功能:用于对容器的遍历
主要方法:
boolean hasNext():判断是否有可以元素继续迭代
Object next():返回迭代的下一个元素
void remove():从迭代器指向的集合中移除迭代器返回的最后一个元素
例子:
Set<String> name = new HashSet<String>(); name.add("LL"); name.add("VV");
name.add("WW"); ...... Iterator<String> it = name.iterator();
while(it.hasNext()){ String n = it.next(); ... }
HashMap相关
HashMap
* 哈希冲突:若干Key的哈希值如果落在同一个数组下标上,将组成一条链,对Key的查找需要遍历链上的每个元素执行equals()比较,1.8后优化为红黑树
* 负载极限
,“负载极限”是一个0~1的数值,“负载极限”决定了hash表的最大填满程度。当hash表中的负载因子达到指定的“负载极限”时,hash表会自动成倍地增加容量(桶的数量),并将原有的对象重新分配,放入新的桶内,这称为rehashing。默认当HashMap中的键值对达到数组大小的75%时,即会rehashing。
解释0.75:
是时间和空间成本上的一种折中:
* 较高的“负载极限”(也就是数组小)可以降低占用的空间,但会增加查询数据的时间开销
* 较低的“负载极限”(也就是数组大)会提高查询数据的性能,但会增加hash表所占用的内存开销
可以根据实际情况来调整“负载极限”值。
多线程比较
HashMap
线程不安全
HashTable
线程安全,实现的方式是在修改数据时锁住整个HashTable,效率低,ConcurrentHashMap做了相关优化
ConcurrentHashMap
线程安全,其关键在于使用了锁分离技术。它使用了多个锁来控制对hash表的不同部分进行的修改。
ConcurrentHashMap内部使用段(Segment)来表示这些不同的部分,每个段其实就是一个小的Hashtable,它们有自己的锁。
只要多个修改操作发生在不同的段上,它们就可以并发进行。
默认将hash表分为16个桶,诸如get、put、remove等常用操作只锁住当前需要用到的桶,读操作大部分时候都不需要用到锁。
(JDK1.8已经摒弃了Segment,并发控制使用Synchronized和CAS来操作,整个看起来就像是优化过且线程安全的HashMap,虽然在JDK1.8中还能看到Segment的数据结构,但是已经简化了属性,只是为了兼容旧版本。)
泛型
泛型用一个通用的数据类型T来代替类,在类实例化时指定T的类型,运行时自动编译为本地代码,运行效率和代码质量都有很大提高,并且保证数据类型安全。
泛型的作用就是提高代码的重用性,避免强制类型转换,减少装箱拆箱提高性能,减少错误。