AI智能
改变未来

给女朋友用来复习Java的精华总结


小白和老手都应该看看的总结

输入

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( 初始化; 终止条件; 更新 20000) {

}

—————————————————————

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 {@Overridepublic String getMessage() {return "message";}@Overridepublic void printStackTrace() {System.out.println(message);}@Overridepublic String toString() {return "message";}}

 

字符串

String

String是字符串final常量类,值一经赋值,其值不可变(指的是所指向的内存值不可修改,但可以改变指向),而且无法被继承。

初始化

•String name= new String(“小猫”);

和创建对象过程没有区别,创建一个新的String对象,并用name指向它。

•String sex = “女”;

过程:

  1. 先在常量池中查找“女”,如果没有则创建对象
  2. 在栈中创建引用sex,
  3. 将sex指向对象“女”
String s1 = "abc";//"abc"是一个对象String s2 = new String("abc");//这里变成两个对象,在内存中存在两个,包括对象“abc” 和 new 出来的对象String s3 = "abc";  //这里的‘abc’ַ和s1的‘abc’是同一个对象,二者的内存地址一样。System.out.println(s1==s2);//falseSystem.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():返回此列表中的元素数
//mapMap<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)//是否包含keyboolean containsValue(Object value)//是否包含valueV get(Object key)//得到key对应的value生成集合:Set<K> keySet()//返回包含所有key的setCollection<V> values()//返回包含所有value的CollectionTreeMap特有: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的类型,运行时自动编译为本地代码,运行效率和代码质量都有很大提高,并且保证数据类型安全。

泛型的作用就是提高代码的重用性,避免强制类型转换,减少装箱拆箱提高性能,减少错误。

兔兔RabbitMQ!!!CSDN认证博客专家Go/GoLangRedisMySQL本人在腾讯总部工作,bat/tmd有五家发过offer。acm/leetcode刷题狂,c /Java/python/go/php各种后端语言都喜欢玩,安卓/node/叶子/jsp等前端也能写,最喜欢鼓捣各种组件(虽然没人用哈哈),各位请多指教。

赞(0) 打赏
未经允许不得转载:爱站程序员基地 » 给女朋友用来复习Java的精华总结