【Java进阶】——java知识精炼,
【Java进阶】——java知识精炼,
第一部分 基础知识点
# java数据类型
1)基本数据类型
2)自动类型转换关系图
#java关键字
详情参见:https://www.cnblogs.com/chenglc/p/6922834.html
#java 关键字volatile
Java语言提供了一种稍弱的同步机制,即volatile变量,用来确保将变量的更新操作通知到其他线程。当把变量声明为volatile类型后,编译器与运行时都会注意到这个变量是共享的,因此不会将该变量上的操作与其他内存操作一起重排序。volatile变量不会被缓存在寄存器或者对其他处理器不可见的地方,因此在读取volatile类型的变量时总会返回最新写入的值。
在访问volatile变量时不会执行加锁操作,因此也就不会使执行线程阻塞,因此volatile变量是一种比sychronized关键字更轻量级的同步机制。
参考地址:https://www.jianshu.com/p/157279e6efdb
#java标识符
用来表示类名,变量名,方法名,类型名,数组名,文件名的有效字符序列称为标识符。
规则:
1.只有字母(区分大小写),下划线,美元符号和数字组成,长度不受限制。
注:字母包括英文26个字母 ,汉字,日文,朝鲜文,俄文,希腊字母等。
2.第一个字母不能是数字。
3.不能是50个关键字里边的;
4.不能是true false null(尽管三个都不是关键字);
#java运算符
算术运算符
算术运算符用在数学表达式中,它们的作用和在数学中的作用一样。下表列出了所有的算术运算符。
操作符 | 描述 | |
---|---|---|
+ | 加法 - 相加运算符两侧的值 | 二元运算符或二目运算符,对两个操作数处理 |
- | 减法 - 左操作数减去右操作数 | |
* | 乘法 - 相乘操作符两侧的值 | |
/ | 除法 - 左操作数除以右操作数 | |
% | 取模 - 左操作数除以右操作数的余数 | |
++ | 自增: 操作数的值增加1 | 一元运算符或单目运算符 |
-- | 自减: 操作数的值减少1 |
关系运算符
下表为Java支持的关系运算符,表格中的实例整数变量A的值为10,变量B的值为20:
运算符 | 描述 | 例子 |
---|---|---|
== | 检查如果两个操作数的值是否相等,如果相等则条件为真。 | (A == B)为假(非真)。 |
!= | 检查如果两个操作数的值是否相等,如果值不相等则条件为真。 | (A != B) 为真。 |
> | 检查左操作数的值是否大于右操作数的值,如果是那么条件为真。 | (A> B)非真。 |
< | 检查左操作数的值是否小于右操作数的值,如果是那么条件为真。 | (A <B)为真。 |
> = | 检查左操作数的值是否大于或等于右操作数的值,如果是那么条件为真。 | (A> = B)为假。 |
<= | 检查左操作数的值是否小于或等于右操作数的值,如果是那么条件为真。 | (A <= B)为真。 |
位运算符
Java定义了位运算符,应用于整数类型(int),长整型(long),短整型(short),字符型(char),和字节型(byte)等类型。下表列出了位运算符的基本运算,假设整数变量A的值为60和变量B的值为13:
操作符 | 描述 | 例子 | 运算分类 |
---|---|---|---|
& | 如果相对应位都是1,则结果为1,否则为0。双目运算符 | (A&B),得到12,即0000 1100 | 按位运算 |
| | 如果相对应位都是0,则结果为0,否则为1。双目运算符 | (A | B)得到61,即 0011 1101 | |
^ | 如果相对应位值相同,则结果为0,否则为1。双目运算符 | (A ^ B)得到49,即 0011 0001 | |
〜 | 按位补运算符翻转操作数的每一位,即0变成1,1变成0。单目运算符 | (〜A)得到-61,即1100 0011 | |
<< | 按位左移运算符。左操作数按位左移右操作数指定的位数。 | A << 2得到240,即 1111 0000 | 位移运算符 |
>> | 按位右移运算符。左操作数按位右移右操作数指定的位数。 | A >> 2得到15即 1111 | |
>>> | 按位右移补零操作符。左操作数的值按右操作数指定的位数右移,移动得到的空位以零填充。 | A>>>2得到15即0000 1111 |
逻辑运算符
下表列出了逻辑运算符的基本运算,假设布尔变量A为真,变量B为假
操作符 | 描述 | 例子 |
---|---|---|
&& | 称为逻辑与运算符。当且仅当两个操作数都为真,条件才为真。 | (A && B)为假。 |
| | | 称为逻辑或操作符。如果任何两个操作数任何一个为真,条件为真。 | (A | | B)为真。 |
! | 称为逻辑非运算符。用来反转操作数的逻辑状态。如果条件为true,则逻辑非运算符将得到false。 |
instanceof 运算符
该运算符用于操作对象实例,检查该对象是否是一个特定类型(类类型或接口类型)。instanceof运算符使用格式如下:
( Object reference variable ) instanceof (class/interface type)
如果运算符左侧变量所指的对象,是操作符右侧类或接口(class/interface)的一个对象,那么结果为真。
#java循环体
一、for(;;)
1. 无限循环:有for(;;)和while(true)两种,还有一种是for(;true;),应该都会使用,这个不做解释。
2.常用循环方法
这个不多做解释,代码如下:
public class ForDemo1 {
public static void main(String[] args) {
int[] arr = {1, 2, 4, 5};
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
3.两个条件变量的循环
代码如下:
public class ForDemo2 {
public static void main(String[] args) {
for (int i=0, j=0;i<2&& j<3;i++,j++) {
System.out.println("hello world");
}
}
}
运行结果是输出两次hello world 。有兴趣的可以把i<2&&j<3改为i<2&j<3看看结果会是什么?
二、for(:)
先上代码:
public class ForDemo3 {
public static void main(String[] args) {
int[] a = {2, 3, 4, 5, 6};
for (int i : a) {
System.out.println(i);
}
}
}
for(int i:a)解释是:先设置一个与a数组里的元素相同的变量,这个变量先等于a数组的第一个元素,然后进入循环体,第二次循环就等于a数组的第二个元素,进入循环体,以此类推。
三、while嵌套for循环
while循环也就不说了吧,就展示一下while嵌套for循环,代码如下:
ublic class WhileDemo {
public static void main(String[] args) {
int a=1;
while(a<9){
if (a==3){
System.out.println(a);
break;
}else {
System.out.println(a);
}
a++;
}
}
}
这个运行结果是1 2 3,看不懂还是去Debug看一下a的值是如何变化的。
四、遍历Collection对象的方式
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
public class ForDemo4 {
public static void main(String[] args) {
String[] a = {"A", "B", "C", "D"};
Collection stringList = Arrays.asList(a);
for(Iterator i = stringList.iterator(); i.hasNext();){
Object j = i.next();
System.out.println(j);
}
}
}
java.util.Collection 是一个集合接口(集合类的一个顶级接口)。它提供了对集合对象进行基本操作的通用接口方法。Collection接口在Java 类库中有很多具体的实现。Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式。其中Iterator就是这个接口中的一种方法,学java的人都熟悉,叫迭代器,i.hasNext()方法返回值是boolean类型,若为true则表示迭代器下一个有值,否则表示迭代完成;i.next()返回值是一个object类型,表示取出迭代器下一个值并赋值给“=”之前。所以就能够给for循环提供循环的条件了。
#java数组
访问控制修饰符
Java中,可以使用访问控制符来保护对类、变量、方法和构造方法的访问。Java 支持 4 种不同的访问权限。
default (即缺省,什么也不写): 在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。
private : 在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类)
public : 对所有类可见。使用对象:类、接口、变量、方法
protected : 对同一包内的类和所有子类可见。使用对象:变量、方法。 注意:不能修饰类(外部类)。
我们可以通过以下表来说明访问权限:
修饰符 | 当前类 | 同一包内 | 子孙类(同一包) | 子孙类(不同包) | 其他包 |
---|---|---|---|---|---|
public |
Y | Y | Y | Y | Y |
protected |
Y | Y | Y | Y/N(说明) | N |
default |
Y | Y | Y | N | N |
private |
Y | N | N | N | N |
# Map集合和Collection集合的区别
Map集合是有Key和Value的,Collection集合是只有Value。
Collection集合底层也是有Key和Value,只是隐藏起来。
# Java常用包有那些?ma li sun
Java.lang
Java.io
Java.sql
Java.util
Java.awt
Java.net
Java.math
#Object类常用方法有那些?when gtc
Equals
Hashcode
toString
wait
notify
clone
getClass
#java字符串问题
1、Java里的char类型能不能存储一个中文字符?答案:https://blog.csdn.net/buqutianya/article/details/80685437
2、十个常见的java字符串问题:https://www.cnblogs.com/liushaobo/p/4380018.html
#String 类
Java字符串类(java.lang.String)是Java中使用最多的类,也是最为特殊的一个类。
String 类相关基础认知:
1、String类是final的,不可被继承。public final class String。
2、String类是的本质是字符数组char[], 并且其值不可改变。private final char value[];
然后打开String类的API文档,可以发现:
3、String类对象有个特殊的创建的方式,就是直接指定比如String x = "abc","abc"就表示一个字符串对象。而x是"abc"对象的地址,也叫做"abc"对象的引用。
4、String对象可以通过“+”串联。串联后会生成新的字符串。也可以通过concat()来串联。
5、创建字符串的方式很多,归纳起来有三类:
其一,使用new关键字创建字符串,比如String s1 = new String("abc");
其二,直接指定。比如String s2 = "abc";
其三,使用串联生成新的字符串。比如String s3 = "ab" + "c";
6、Java运行时会维护一个String Pool(String池),JavaDoc翻译很模糊“字符串缓冲区”。String池用来存放运行时中产生的各种字符串,并且池中的字符串的内容不重复。而一般对象不存在这个缓冲池,并且创建的对象仅仅存在于方法的堆栈区。
对象创建
字符串对象创建的原理
原理1:当使用任何方式来创建一个字符串对象s时,Java运行时(运行中JVM)会拿着这个X在String池中找是否存在内容相同的字符串对象,如果不存在,则在池中创建一个字符串s,否则,不在池中添加。
原理2:Java中,只要使用new关键字来创建对象,则一定会(在堆区或栈区)创建一个新的对象。
原理3:使用直接指定或者使用纯字符串串联来创建String对象,则仅仅会检查维护String池中的字符串,池中没有就在池中创建一个,有则罢了!但绝不会在堆栈区再去创建该String对象。
原理4:使用包含变量的表达式来创建String对象,则不仅会检查维护String池,而且还会在堆栈区创建一个String对象。
#探秘Java中的String、StringBuilder以及StringBuffer
详情参见:https://www.cnblogs.com/dolphin0520/p/3778589.html
# String str=”aaa”,与String str=new String(“aaa”)一样吗?
不一样的。因为内存分配的方式不一样。
第一种,创建的”aaa”是常量,jvm都将其分配在常量池中。
第二种创建的是一个对象,jvm将其值分配在堆内存中。
# String str=”aa”,String s=”bb”,String aa=aa+s;一种创建了几个对象?
一共有两个引用,三个对象。因为”aa”与”bb”都是常量,常量的值不能改变,当执行字符串拼接时候,会创建一个新的常量是” aabbb”,有将其存到常量池中。
# String类的常用方法有那些?
charAt:返回指定索引处的字符
indexOf():返回指定字符的索引
replace():字符串替换
trim():去除字符串两端空白
split():分割字符串,返回一个分割后的字符串数组
getBytes():返回字符串的byte类型数组
length():返回字符串长度
toLowerCase():将字符串转成小写字母
toUpperCase():将字符串转成大写字符
substring():截取字符串
format():格式化字符串
equals():字符串比较
# Java 集合框架
整个集合框架就围绕一组标准接口而设计。你可以直接使用这些接口的标准实现,诸如: LinkedList, HashSet, 和 TreeSet 等,除此之外你也可以通过这些接口实现自己的集合。
从上面的集合框架图可以看到,Java 集合框架主要包括两种类型的容器,一种是集合(Collection),存储一个元素集合,另一种是图(Map),存储键/值对映射。Collection 接口又有 3 种子类型,List、Set 和 Queue,再下面是一些抽象类,最后是具体实现类,常用的有 ArrayList、LinkedList、HashSet、LinkedHashSet、HashMap、LinkedHashMap 等等。
集合框架是一个用来代表和操纵集合的统一架构。所有的集合框架都包含如下内容:
接口:是代表集合的抽象数据类型。例如 Collection、List、Set、Map 等。之所以定义多个接口,是为了以不同的方式操作集合对象
实现(类):是集合接口的具体实现。从本质上讲,它们是可重复使用的数据结构,例如:ArrayList、LinkedList、HashSet、HashMap。
算法:是实现集合接口的对象里的方法执行的一些有用的计算,例如:搜索和排序。这些算法被称为多态,那是因为相同的方法可以在相似的接口上有着不同的实现。
除了集合,该框架也定义了几个 Map 接口和类。Map 里存储的是键/值对。尽管 Map 不是集合,但是它们完全整合在集合中。
集合框架体系如图所示
Java 集合框架提供了一套性能优良,使用方便的接口和类,java集合框架位于java.util包中, 所以当使用集合框架的时候需要进行导包。
#Java 中区分 API 和 SPI
通俗的讲:API 和 SPI 都是相对的概念,他们的差别只在语义上,API 直接被应用开发人员使用,SPI 被框架扩展人员使用。
参考链接:https://www.jianshu.com/p/7e85b8ed00e2
# Set里的元素是不能重复的,用什么方法来区分重复与否呢? 是用==还是equals()?
答: Set里的元素是不能重复的,那么用iterator()方法来区分重复与否。equals()是判读两个Set是否相等 equals()和==方法决定引用值是否指向同一对象equals()在类中被覆盖,为的是当两个分离的对象的内容和类型相配的话,返回真值
# HashMap和Hashtable的区别:
HashMap和Hashtable都实现了Map接口,但决定用哪一个之前先要弄清楚它们之间的分别。主要的区别有:线程安全性,同步(synchronization),以及速度。
1、HashMap几乎可以等价于Hashtable,除了HashMap是非synchronized的,并可以接受null(HashMap可以接受为null的键值(key)和值(value),而Hashtable则不行)。
2、HashMap是非synchronized,而Hashtable是synchronized,这意味着Hashtable是线程安全的,多个线程可以共享一个Hashtable;而如果没有正确的同步的话,多个线程是不能共享HashMap的。Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。
3、另一个区别是HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。所以当有其它线程改变了HashMap的结构(增加或者移除元素),将会抛出ConcurrentModificationException,但迭代器本身的remove()方法移除元素则不会抛出ConcurrentModificationException异常。但这并不是一个一定发生的行为,要看JVM。这条同样也是Enumeration和Iterator的区别。
4、由于Hashtable是线程安全的也是synchronized,所以在单线程环境下它比HashMap要慢。如果你不需要同步,只需要单一线程,那么使用HashMap性能要好过Hashtable。
5、HashMap不能保证随着时间的推移Map中的元素次序是不变的。
# Java 接口
接口(英文:Interface),在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。除非实现接口的类是抽象类,否则该类要定义接口中的所有方法。接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。另外,在 Java 中,接口类型可用来声明一个变量,他们可以成为一个空指针,或是被绑定在一个以此接口实现的对象。
接口与类相似点:
- 一个接口可以有多个方法。
- 接口文件保存在 .java 结尾的文件中,文件名使用接口名。
- 接口的字节码文件保存在 .class 结尾的文件中。
- 接口相应的字节码文件必须在与包名称相匹配的目录结构中。
接口与类的区别:
- 接口不能用于实例化对象。
- 接口没有构造方法。
- 接口中所有的方法必须是抽象方法。
- 接口不能包含成员变量,除了 static 和 final 变量。
- 接口不是被类继承了,而是要被类实现。
- 接口支持多继承。
接口特性
- 接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错)。
- 接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量(并且只能是 public,用 private 修饰会报编译错误)。
- 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法。
抽象类和接口的区别
- 1. 抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。
- 2. 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。
- 3. 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。
- 4. 一个类只能继承一个抽象类,而一个类却可以实现多个接口。
# 抽象类可以使用final修饰吗?
不可以。定义抽象类就是让其他继承的,而final修饰类表示该类不能被继承,与抽象类的理念违背了
# 内部类与静态内部类的区别?
1、静态内部类相对与外部类是独立存在的,在静态内部类中无法直接访问外部类中变量、方法。如果要访问的话,必须要new一个外部类的对象,使用new出来的对象来访问。但是可以直接访问静态的变量、调用静态的方法;
2、普通内部类作为外部类一个成员而存在,在普通内部类中可以直接访问外部类属性,调用外部类的方法。
3、如果外部类要访问内部类的属性或调用内部类的方法,必须要创建一个内部类的对象,使用该对象访问属性或者调用方法。
4、如果其他的类要访问普通内部类的属性或者调用普通内部类的方法,必须要在外部类中创建一个普通内部类的对象作为一个属性,外同类可以通过该属性调用普通内部类的方法或者访问普通内部类的属性
5、如果其他的类要访问静态内部类的属性或者调用静态内部类的方法,直接创建一个静态内部类对象即可。
# 当父类引用指向子类对象的时候,子类重写了父类方法和属性,那么当访问属性的时候,访问是谁的属性?调用方法时,调用的是谁的方法?
子类重写了父类方法和属性,访问的是父类的属性,调用的是子类的方法
#java构造函数的深入理解
参考文章:https://www.cnblogs.com/huxins/p/9017212.html
# 异常机制
Java体系中异常的组织分类如下图所示,所有异常类型的根类为 Throwable,具体包括两大类:Error 与 Exception。其中,Error是指程序无法处理的错误,表示运行应用程序中较严重问题;Exception是指程序本身可以处理的错误,具体可分为运行时异常(派生于 RuntimeException 的异常)和其他异常。
此外,从异常是否必须需要被处理的角度来看,异常又可分为不受检查异常和受检查异常两种情况:
(1)不受检查异常:派生于 Error 或 RuntimeException 的所有异常。
(2)受检查异常:除去不受检查异常的所有异常。
finally子句,在对应的try子句执行的前提下,finally 子句总会被执行。并且,finally子句 总是在诸如return、break、throw和continue等控制转移语句之前执行。
# 说几个常见的编译时异常类?
NullPointerException:空指针异常
ArrayIndexOutOfBoundsException:数组下标越界
NumberFormatException:数字转换异常
IllegalArgumentException:参数不匹配异常
InstantiationException:对象初始化异常
ArithmeticException:算术异常
# Try.catch.finally是必须要存在的吗?
Try块必须存在,catch和finally可以不存在,但不能同时不存在
# Thow与thorws区别
Throw写在代码块内,throw后面跟的是一个具体的异常实例
Throw写在方法前面后面,throws后面跟的是异常类,异常类可以出现多个
# Error与Exception区别?
Error和Exception都是java错误处理机制的一部分,都继承了Throwable类。
Exception表示的异常,异常可以通过程序来捕捉,或者优化程序来避免。
Error表示的是系统错误,不能通过程序来进行错误处理。
# final、finalize()、finally
性质不同
作用
# 线程与进程的区别
进程是系统进行资源分配和调度的一个独立单位,线程是CPU调度和分派的基本单位
进程和线程的关系:
线程与进程的区别:
#java多线程和python多线程的区别
1、python的多线程的问题:GIL导致PYTHON 无法使用到计算机的多核,仅能使用单核
2、 JAVA传统的多线程主要解决的问题:
1)运行于多核CPU上,各线程可分布于CPU的各个核心,让程序真正的并发
2)因为外设(IO外设)的速度不匹配,导致线程阻塞。所以需要多线程切换来让阻塞的线程让出CPU,让其它线程运行。
# Thread 类中的start() 和 run() 方法有什么区别?
start() :
它的作用是启动一个新线程。
通过start()方法来启动的新线程,处于就绪(可运行)状态,并没有运行,一旦得到cpu时间片,就开始执行相应线程的run()方法,这里方法run()称为线程体,它包含了要执行的这个线程的内容,run方法运行结束,此线程随即终止。start()不能被重复调用。用start方法来启动线程,真正实现了多线程运行,即无需等待某个线程的run方法体代码执行完毕就直接继续执行下面的代码。这里无需等待run方法执行完毕,即可继续执行下面的代码,即进行了线程切换。
run() :
run()就和普通的成员方法一样,可以被重复调用。
如果直接调用run方法,并不会启动新线程!程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待run方法体执行完毕后才可继续执行下面的代码,这样就没有达到多线程的目的。
总结:调用start方法方可启动线程,而run方法只是thread的一个普通方法调用,还是在主线程里执行。
public class Test {
static void pong(){
System.out.print("pong");
}
public static void main(String[] args) {
Thread t=new Thread(){
public void run(){
pong();
}
};
t.run();
System.out.print("ping");
}
}
运行结果:
pongping
总结一下:
# Java中如何停止一个线程?
Java提供了很丰富的API但没有为停止线程提供API。JDK 1.0本来有一些像stop(), suspend() 和 resume()的控制方法但是由于潜在的死锁威胁因此在后续的JDK版本中他们被弃用了,之后Java API的设计者就没有提供一个兼容且线程安全的方法来停止一个线程。当run() 或者 call() 方法执行完的时候线程会自动结束,如果要手动结束一个线程,你可以用volatile 布尔变量来退出run()方法的循环或者是取消任务来中断线程。
# 多线程知识点
参考:https://blog.csdn.net/lovezhaohaimig/article/details/80287888
#快速失败(fail-fast)和安全失败(fail-safe)的区别
Iterator的安全失败是基于对底层集合做拷贝,因此,它不受源集合上修改的影响。java.util包下面的所有的集合类都是快速失败的,而java.util.concurrent包下面的所有的类都是安全失败的。快速失败的迭代器会抛出ConcurrentModificationException异常,而安全失败的迭代器永远不会抛出这样的异常。fail-fast机制,是一种错误检测机制。它只能被用来检测错误,因为JDK并不保证fail-fast机制一定会发生。若在多线程环境下使用fail-fast机制的集合,建议使用“java.util.concurrent包下的类”去取代“java.util包下的类”。
详情参见:https://blog.csdn.net/u010889616/article/details/79954413
#什么是JVM?java虚拟机包括什么?
参考文章: https://blog.csdn.net/qq_41701956/article/details/81664921
#java内存管理机制
详情参见:https://www.cnblogs.com/KingIceMou/p/6967129.html
#java获取dump文件
Dump文件主要是将内存中的内容储存起来的物理文件,根据储存的不同内存段,可以将dump文件分为内核模式dump(Kernel-mode dump)和用户模式dump(User-mode dump),我们主要用到是用户模式dump。用户模式dump又可以分为完全dump(Full User-Mode Dump)和迷你dump(Minidump),minidump只包含目标进程相关的内存,full dump包含了所有用户空间的内存,同时还包括了虚拟内存,所以minidump远远小于full dump,我们常用的是迷你dump。
dump 分析及获取工具
jstack、jstat、jmap、jhat、Arthas
参考文章:https://www.jianshu.com/p/a6b3d0c7a707
#Java服务程序突然变慢,如何定位?
- 检查网络是否存在问题
最先排查的原因就应该是网络问题,即外部因素。常见的着手方法是测试网速,这里推荐工具speedtest,当然类似的有很多:
1)安装:
$ wget https:
//raw
.githubusercontent.com
/sivel/speedtest-cli/master/speedtest
.py
$ chmod
a+rx speedtest.py
$
mv
speedtest.py
/usr/local/bin/speedtest
$
chown
root:root
/usr/local/bin/speedtest
2)调用命令测试
$ speedtest
转自:https://blog.csdn.net/Beyond_F4/article/details/80497118
2. 检查日志
首先检查程序日志,看有哪些异常被抛出,有没有类似内部错误的不正常的异常被抛出;
如果使用tomcat部署,还需检查tomcat的日志。
3. 测试响应时间
如果以上的都没有问题,可以再次测试程序响应时间,注意:主要是为了测试服务响应时间,即服务处理请求构造数据消耗的时间。
通过curl得到http各阶段的响应时间,这可以参考:https://blog.csdn.net/hqzxsc2006/article/details/50547684
4. 定位线程
查看内存使用:top、free、ps、cat /proc/meminfo
查看磁盘使用:df -h ,例如日志写完等等
尝试查看定位到具体的线程查看原因:
1) ps -ef | grep java 找出最耗CPU的JAVA进程(一般就是服务进程引起)
2) top -Hp "进程ID" 找出最耗时间的JAVA线程
3) jstack "进程ID" | grep "线程ID"
Java 的引用类型有哪几种
对象的强、软、弱和虚引用(四种引用)
在JDK 1.2以前的版本中,若一个对象不被任何变量引用,那么程序就无法再使用这个对象。也就是说,只有对象处于可触及(reachable)状态,程序才能使用它。从JDK 1.2版本开始,把对象的引用分为4种级别,从而使程序能更加灵活地控制对象的生命周期。这4种级别由高到低依次为:强引用、软引用、弱引用和虚引用。
⑴强引用(StrongReference)
强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。 ps:强引用其实也就是我们平时A a = new A()这个意思。
⑵软引用(SoftReference)
如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存(下文给出示例)。
软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收器回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。
⑶弱引用(WeakReference)
弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。
⑷虚引用(PhantomReference)
“虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。虚引用主要用来跟踪对象被垃圾回收器回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列 (ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之 关联的引用队列中。
ReferenceQueue queue = new ReferenceQueue ();
PhantomReference pr = new PhantomReference (object, queue);
程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。
# 类加载器实例化时进行的操作顺序(加载–>连接->初始化)
父类静态代变量、
父类静态代码块、
子类静态变量、
子类静态代码块、
父类非静态变量(父类实例成员变量)、
父类构造函数、
子类非静态变量(子类实例成员变量)、
子类构造函数。
# 一个线程的生命周期
线程是一个动态执行的过程,它也有一个从产生到死亡的过程。下图显示了一个线程完整的生命周期。
- 新建状态:
使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。
- 就绪状态:
当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。
- 运行状态:
如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。
- 阻塞状态:
如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:
-
等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。
- 同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。
- 其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。
- 死亡状态:
一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。
#Java并发编程:Callable、Future和FutureTask
创建线程的2种方式,一种是直接继承Thread,另外一种就是实现Runnable接口。这2种方式都有一个缺陷就是:在执行完任务之后无法获取执行结果。如果需要获取执行结果,就必须通过共享变量或者使用线程通信的方式来达到效果,这样使用起来就比较麻烦。而自从Java 1.5开始,就提供了Callable和Future,通过它们可以在任务执行完毕之后得到任务执行结果。
参考文章:http://www.cnblogs.com/dolphin0520/p/3949310.html
# 创建一个线程
Java 提供了三种创建线程的方法:
- 通过实现 Runnable 接口;
- 通过继承 Thread 类本身;
- 通过 Callable 和 Future 创建线程。
#写一个简单死锁的程序
/**
* 一个简单的死锁类
* 当DeadLock类的对象flag==1时(td1),先锁定o1,睡眠500毫秒
* 而td1在睡眠的时候另一个flag==0的对象(td2)线程启动,先锁定o2,睡眠500毫秒
* td1睡眠结束后需要锁定o2才能继续执行,而此时o2已被td2锁定;
* td2睡眠结束后需要锁定o1才能继续执行,而此时o1已被td1锁定;
* td1、td2相互等待,都需要得到对方锁定的资源才能继续执行,从而死锁。
*/
public class DeadLock implements Runnable {
public int flag = 1;
//静态对象是类的所有对象共享的
private static Object o1 = new Object(), o2 = new Object();
@Override
public void run() {
System.out.println("flag=" + flag);
if (flag == 1) {
synchronized (o1) {
try {
Thread.sleep(500);
} catch (Exception e) {
e.printStackTrace();
}
synchronized (o2) {
System.out.println("1");
}
}
}
if (flag == 0) {
synchronized (o2) {
try {
Thread.sleep(500);
} catch (Exception e) {
e.printStackTrace();
}
synchronized (o1) {
System.out.println("0");
}
}
}
}
public static void main(String[] args) {
DeadLock td1 = new DeadLock();
DeadLock td2 = new DeadLock();
td1.flag = 1;
td2.flag = 0;
//td1,td2都处于可执行状态,但JVM线程调度先执行哪个线程是不确定的。
//td2的run()可能在td1的run()之前运行
new Thread(td1).start();
new Thread(td2).start();
}
}
#synchronized 和 lock 之间关系
synchronized是基于jvm底层实现的数据同步,lock是基于Java编写,主要通过硬件依赖CPU指令实现数据同步。
参考文章:https://www.cnblogs.com/jiangds/p/6476293.html
#springCloud是什么?
springcloud是一个微服务框架,并提供全套分布式系统解决方案。支持配置管理,熔断机制,leader选举,服务治理,分布式session,微代理,控制总线,智能路由,一次性token。
#Spring的Scope有以下几种,通过@Scope注解来实现:
(1)Singleton:一个Spring容器中只有一个Bean的实例,此为Spring的默认配置,全容器共享一个实例。
(2)Prototype:每次调用新建一个Bean实例。
(3)Request:Web项目中,给每一个 http request 新建一个Bean实例。
(4)Session:Web项目中,给每一个 http session 新建一个Bean实例。
(5)GlobalSession:这个只在portal应用中有用,给每一个 global http session 新建一个Bean实例。
# spring MVC与struts2的区别:
1、Struts2是类级别的拦截, 一个类对应一个request上下文,SpringMVC是方法级别的拦截
2、SpringMVC的方法之间基本上独立的,独享request response数据
3、由于Struts2需要针对每个request进行封装,把request,session等servlet生命周期的变量封装成一个一个
Map,供给每个Action使用,并保证线程安全,所以在原则上,是比较耗费内存的
4、拦截器实现机制上,Struts2有以自己的interceptor机制,SpringMVC用的是独立的AOP方式
5、SpringMVC的入口是servlet,而Struts2是filter
6、SpringMVC集成了Ajax
7、SpringMVC验证支持JSR303,处理起来相对更加灵活方便,而Struts2验证比较繁琐,感觉太烦乱
8、Spring MVC和Spring是无缝的。从这个项目的管理和安全上也比Struts2高
9、Struts2更加符合OOP的编程思想, SpringMVC就比较谨慎,在servlet上扩展
10、SpringMVC开发效率和性能高于Struts2
11、SpringMVC可以认为已经100%零配置
#java内存区域和内存溢出
参考文章:http://wiki.jikexueyuan.com/project/java-vm/storage.html
#MYSQL 有哪些存储引擎,各自优缺点
MyISAM: 拥有较高的插入,查询速度,但不支持事务 ;
InnoDB :5.5版本后Mysql的默认数据库,事务型数据库的首选引擎,支持ACID事务,支持行级锁定 ;
BDB: 源自Berkeley DB,事务型数据库的另一种选择,支持COMMIT和ROLLBACK等其他事务特性 ;
Memory :数据置于内存的存储引擎,极高的插入/更新/查询效率。会占用和数据量成正比的内存空间。其内容会在重启时丢失
Merge :将一定数量的MyISAM表联合而成一个整体,在超大规模数据存储时很有用 ;
Archive :非常适合存储大量独立的,作为历史记录的数据。其拥有高效的插入速度,但其对查询的支持相对较差 ;
Federated: 将不同的Mysql服务器联合起来,逻辑上组成一个完整的数据库。非常适合分布式应用
Cluster/NDB :高冗余的存储引擎,用多台数据机器联合提供服务。适合数据量大,安全和性能要求高的应用 ;
CSV: 逻辑上由逗号分割数据的存储引擎,会在数据库子目录里为每个数据表创建一个.CSV文件。CSV存储引擎不支持索引;
BlackHole :黑洞引擎,写入的任何数据都会消失,一般用于记录binlog做复制的中继 ;
# JDBC操作的步骤
加载数据库驱动类
打开数据库连接
执行sql语句
处理返回结果
关闭资源
# 在使用jdbc的时候,如何防止出现sql注入的问题
使用PreparedStatement类,而不是使用Statement类
参考文章:https://www.jb51.net/article/108987.htm
#怎么在JDBC内调用一个存储过程
使用CallableStatement
参考文章:https://www.cnblogs.com/fjdingsd/p/5272923.html
# 是否了解连接池,使用连接池有什么好处?
数据库连接是非常消耗资源的,影响到程序的性能指标。连接池是用来分配、管理、释放数据库连接的,可以使应用程序重复使用同一个数据库连接,而不是每次都创建一个新的数据库连接。通过释放空闲时间较长的数据库连接避免数据库因为创建太多的连接而造成的连接遗漏问题,提高了程序性能。
参考文章:https://www.cnblogs.com/sharpest/p/6240475.html
#ACID
A:atomic,原子性,要么都提交,要么都失败,不能一部分成功,一部分失败。
C:consistent,一致性,事物开始及结束后,数据的一致性约束没有被破坏
I:isolation,隔离性,并发事物间相互不影响,互不干扰。
D:durability,持久性,已经提交的事物对数据库所做的更新必须永久保存。即便发生崩溃,也不能被回滚或数据丢失。
# 简单说一下数据库的三范式?
第一范式:数据库表的每一个字段都是不可分割的
第二范式:数据库表中的非主属性只依赖于主键
第三范式:不存在非主属性对关键字的传递函数依赖关系
# 查询中哪些情况不会使用索引?
https://www.cnblogs.com/xixibaby/p/6409928.html
# 数据库索引,底层是怎样实现的,为什么要用B树索引?
https://blog.csdn.net/qq_35571554/article/details/82796278
# 怎么对数据库百万级数据进行优化?
使用读写分离技术(
让主数据库(master)处理事务性增、改、删操作(INSERT、UPDATE、DELETE),而从数据库(slave)处理SELECT查询操作
)
# 谈谈你对Spring的理解
1.Spring是实现了工厂模式的工厂类(在这里有必要解释清楚什么是工厂模式),这个类名为BeanFactory(实际上是一个接口),在程序中通常BeanFactory的子类ApplicationContext。Spring相当于一个大的工厂类,在其配置文件中通过<bean>元素配置用于创建实例对象的类名和实例对象的属性。
2. Spring提供了对IOC良好支持,IOC是一种编程思想,是一种架构艺术,利用这种思想可以很好地实现模块之间的解耦,IOC也称为DI(Depency Injection)。
3. Spring提供了对AOP技术的良好封装, AOP称为面向切面编程,就是系统中有很多各不相干的类的方法,在这些众多方法中要加入某种系统功能的代码,例如,加入日志,加入权限判断,加入异常处理,这种应用称为AOP。
实现AOP功能采用的是代理技术,客户端程序不再调用目标,而调用代理类,代理类与目标类对外具有相同的方法声明,有两种方式可以实现相同的方法声明,一是实现相同的接口,二是作为目标的子类。
在JDK中采用Proxy类产生动态代理的方式为某个接口生成实现类,如果要为某个类生成子类,则可以用CGLI B。在生成的代理类的方法中加入系统功能和调用目标类的相应方法,系统功能的代理以Advice对象进行提供,显然要创建出代理对象,至少需要目标类和Advice类。spring提供了这种支持,只需要在spring配置文件中配置这两个元素即可实现代理和aop功能。
# Spring Bean的生命周期:
1、Bean的建立, 由BeanFactory读取Bean定义文件,并生成各个实例
2、Setter注入,执行Bean的属性依赖注入
3、BeanNameAware的setBeanName(), 如果实现该接口,则执行其setBeanName方法
4、BeanFactoryAware的setBeanFactory(),如果实现该接口,则执行其setBeanFactory方法
5、BeanPostProcessor的processBeforeInitialization(),如果有关联的processor,则在Bean初始化之前都会执行这个实例的processBeforeInitialization()方法
6、InitializingBean的afterPropertiesSet(),如果实现了该接口,则执行其afterPropertiesSet()方法
7、Bean定义文件中定义init-method
8、BeanPostProcessors的processAfterInitialization(),如果有关联的processor,则在Bean初始化之前都会执行这个实例的processAfterInitialization()方法
9、DisposableBean的destroy(),在容器关闭时,如果Bean类实现了该接口,则执行它的destroy()方法
10、Bean定义文件中定义destroy-method,在容器关闭时,可以在Bean定义文件中使用“destory-method”定义的方法
# Spring中IOC的理解
(1)超级大工厂:对象控制权由调用者移交给容器,使得调用者不必关心对象的创建和管理,专注于业务逻辑开发;
(2)优秀的解耦方式,解耦对象间的依赖关系,避免通过硬编码的方式耦合在一起;
(3)底层实现:反射机制;
# Spring中AOP的理解
(1)一种新的模块化方式,专门处理系统各模块中的交叉关注点问题,将具有横切性质的系统级业务提取到切面中,与核心业务逻辑分离(解耦);
(2)便于系统的扩展,符合开-闭原则;
(3)动态AOP的实现,Java动态代理(接口代理)与cglib(类代理),具体由Bean后处理器生成代理;
(4)AOP理念实践:Spring AOP,Java Web Filter,Struts2 Interceptor, SpringMVC Interceptor,…
# AJAX有哪些有点和缺点
优点:
1、最大的一点是页面无刷新,用户的体验非常好。
2、使用异步方式与服务器通信,具有更加迅速的响应能力。
3、可以把以前一些服务器负担的工作转嫁到客户端,利用客户端闲置的能力来处理,减轻服务器和带宽的负担,节约空间和宽带租用成本。并且减轻服务器的负担,ajax的原则是“按需取数据”,可以最大程度的减少冗余请求,和响应对服务器造成的负担。
4、基于标准化的并被广泛支持的技术,不需要下载插件或者小程序。
缺点:
1、ajax不支持浏览器back按钮。
2、安全问题 AJAX暴露了与服务器交互的细节。
3、对搜索引擎的支持比较弱。
4、破坏了程序的异常机制。
5、不容易调试。
# https和http区别,有没有用过其他安全传输手段?
https://blog.csdn.net/rainHistory/article/details/78780227
#六大设计原则
(1)单一职责原则:高内聚,一个类只做它该做的事情;
(2)接口隔离原则:接口小而专,避免大而全;
(3)依赖倒置原则:依赖抽象而非实现,面向接口编程;
(4)里氏替换原则:子类可以扩展父类的功能,但不能改变父类原有的功能;
(5)开闭原则:Open for Extension, Closed for Modification,例如AOP,代理模式,适配器模式就是其经典应用;
(6)迪米特法则:高内聚,低耦合;
# 内部类
内部类指的是在一个类的内部所定义的类,类名不需要和源文件名相同。在Java中,内部类是一个编译时的概念,一旦编译成功,内部类和外部类就会成为两个完全不同的类,共有四种类型:
(1)成员内部类:成员内部类是外围类的一个成员,是依附于外围类的,所以,只有先创建了外围类对象才能够创建内部类对象。也正是由于这个原因,成员内部类也不能含有 static 的变量和方法;
(2)静态内部类:静态内部类,就是修饰为static的内部类,该内部类对象不依赖于外部类对象,就是说我们可以直接创建内部类对象,但其只可以直接访问外部类的所有静态成员和静态方法;
(3)局部内部类:局部内部类和成员内部类一样被编译,只是它的作用域发生了改变,它只能在该方法和属性中被使用,出了该方法和属性就会失效;
(4)匿名内部类:定义匿名内部类的前提是,内部类必须要继承一个类或者实现接口,格式为 new 父类或者接口(){定义子类的内容(如函数等)}。也就是说,匿名内部类最终提供给我们的是一个匿名子类的对象。
# 进阶必读阅读源代码
String、Integer、Long、Enum、
BigDecimal、ThreadLocal、ClassLoader & URLClassLoader、
ArrayList & LinkedList、
HashMap & LinkedHashMap & TreeMap & CouncurrentHashMap、HashSet & LinkedHashSet & TreeSet
# Tomcat,apache,jboss的区别
Tomcat是servlet容器,用于解析jsp,servlet。是一个轻量级的高效的容器;缺点是不支持EJB,只能用于Java应用。Apache是http服务器(web服务器),类似于IIS可以用来建立虚拟站点,编译处理静态页面。支持SSL技术,支持多个虚拟主机等功能。Jboss是应用服务器,运行EJB的javaee应用服务器,遵循javaee规范,能够提供更多平台的支持和更多集成功能,如数据库连接,JCA等。其对servlet的支持是通过集成其他servlet容器来实现的。如tomcat。
# SESSION, COOKIE区别
session数据放在服务器上,cookie则放在客户浏览器上。cookie不太安全,因为可以分析出本地cookie,并进行cookie欺骗,考虑安全应使用session。session会在一定时间内保存在服务器上,当访问增多时,会比较占用服务器的性能,考虑减轻服务器压力则应该使用cookie。单个cookie保持的数据不超过4k,很多浏览器都限制要给站点最多保存20个cookie。
# 谈谈Hibernate的理解
1. 面向对象设计的软件内部运行过程可以理解成就是在不断创建各种新对象、建立对象之间的关系,调用对象的方法来改变各个对象的状态和对象消亡的过程,不管程序运行的过程和操作怎么样,本质上都是要得到一个结果,程序上一个时刻和下一个时刻的运行结果的差异就表现在内存中的对象状态发生了变化。
2.为了在关机和内存空间不够的状况下,保持程序的运行状态,需要将内存中的对象状态保存到持久化设备和从持久化设备中恢复出对象的状态,通常都是保存到关系数据库来保存大量对象信息。从Java程序的运行功能上来讲,保存对象状态的功能相比系统运行的其他功能来说,应该是一个很不起眼的附属功能,java采用jdbc来实现这个功能,这个不起眼的功能却要编写大量的代码,而做的事情仅仅是保存对象和恢复对象,并且那些大量的jdbc代码并没有什么技术含量,基本上是采用一套例行公事的标准代码模板来编写,是一种苦活和重复性的工作。
3.通过数据库保存java程序运行时产生的对象和恢复对象,其实就是实现了java对象与关系数据库记录的映射关系,称为ORM(即Object RelationMapping),人们可以通过封装JDBC代码来实现了这种功能,封装出来的产品称之为ORM框架,Hibernate就是其中的一种流行ORM框架。使用Hibernate框架,不用写JDBC代码,仅仅是调用一个save方法,就可以将对象保存到关系数据库中,仅仅是调用一个get方法,就可以从数据库中加载出一个对象。
4.使用Hibernate的基本流程是:配置Configuration对象、产生SessionFactory、创建session对象,启动事务,完成CRUD操作,提交事务,关闭session。
5.使用Hibernate时,先要配置hibernate.cfg.xml文件,其中配置数据库连接信息和方言等,还要为每个实体配置相应的hbm.xml文件,hibernate.cfg.xml文件中需要登记每个hbm.xml文件。
6.在应用Hibernate时,重点要了解Session的缓存原理,级联,延迟加载和hql查询。
# MVC的各个部分都有那些技术来实现?如何实现?
MVC是Model-View-Controller的简写。Model代表的是应用的业务逻辑(通过JavaBean,EJB组件实现),View是应用的表示面(由JSP页面产生),Controller是提供应用的处理过程控制(一般是一个Servlet),通过这种设计模型把应用逻辑,处理过程和显示逻辑分成不同的组件实现。这些组件可以进行交互和重用。
# Http中,get和post方法的区别
1,Get是向服务器发索取数据的一种请求,而Post是向服务器提交数据的一种请求
2,Get是获取信息,而不是修改信息,类似数据库查询功能一样,数据不会被修改
3,Get请求的参数会跟在url后进行传递,请求的数据会附在URL之后,以?分割URL和传输数据,参数之间以&相连,%XX中的XX为该符号以16进制表示的ASCII,如果数据是英文字母/数字,原样发送,如果是空格,转换为+,如果是中文/其他字符,则直接把字符串用BASE64加密。
4,Get传输的数据有大小限制,因为GET是通过URL提交数据,那么GET可提交的数据量就跟URL的长度有直接关系了,不同的浏览器对URL的长度的限制是不同的。
5,GET请求的数据会被浏览器缓存起来,用户名和密码将明文出现在URL上,其他人可查到历史浏览记录,数据不安全。在服务器端,用Request.QueryString来获取Get方式提交来的数据Post请求则作为http消息的实际内容发送给web服务器,数据放置在HTML Header内提交,Post没有限制提交的数据。Post比Get安全,当数据是中文或者不敏感的数据,则用get,因为使用get,参数会显示在地址,对于敏感数据和不是中文字符的数据,则用post。
6,POST表示可能修改变服务器上的资源的请求,在服务器端,用Post方式提交的数据只能用Request.Form来获取。
# jsp和servlet的区别、共同点、各自应用的范围?
1、 JSP是Servlet技术的扩展,本质上就是Servlet的简易方式。JSP编译后是“类servlet”。
2、Servlet和JSP最主要的不同点在于:Servlet的应用逻辑是在Java文件中,并且完全从表示层中的HTML里分离开来。而JSP的情况是Java和HTML可以组合成一个扩展名为.jsp的文件。
3、JSP侧重于视图,Servlet主要用于控制逻辑。在struts框架中,JSP位于MVC设计模式的视图层,而Servlet位于控制层.
# 什么是幻读,哪种隔离级别可以防止幻读?
幻读是指一个事务多次执行一条查询返回的却是不同的值。假设一个事务正根据某个条件进行数据查询,然后另一个事务插入了一行满足这个查询条件的数据。之后这个事务再次执行了这条查询,返回的结果集中会包含刚插入的那条新数据。这行新数据被称为幻行,而这种现象就叫做幻读。
只有TRANSACTION_SERIALIZABLE隔离级别才能防止产生幻读。
# 描述struts的工作流程
简略过程就是web应用启动,接收用户请求并进行匹配,返回用户请求信息。
1. 在web应用启动时,加载并初始化ActionServlet,ActionServlet从struct-config.xml文件中读取配置信息,把它们存放到各个配置对象中。
2. 当ActionServlet接收到一个客户请求时,首先检索和用户请求相配的ActionMapping实例,如果不存在,返回用户请求路径无效信息。
3. 如ActionForm实例不存在,则创建一个ActionForm对象,把客户提交的表单数据保存到ActionForm对象中。
4. 根据配置信息决定是否需要表单验证。如果需要验证,就调用ActionForm的Validate()方法。如果Valiedate()方法返回null或返回一个不包含ActionMessage的ActionErrors对象,则表示表单验证成功。
5. ActionServlet更加ActionMapping实例包含的映射信息决定请请求转发给哪个Action。如果相应的Action实例不存在,则先创建这个实例,然后调用Action的execute()方法。
6. Action的execute()方法返回一个ActionForward对象,ActionServlet再把客户请求转发给ActionForward对象指向的JSP组建。
7. ActionForward对象指向的jsp组件生成的动态网页,返回给客户。
# Tomcat的session处理,如果让你实现一个tomcatserver,如何实现session机制
当一个session开始时,Servlet容器会创建一个HttpSession对象,在某些情况下把这些HttpSession对象从内存中转移到文件系统中或数据库中。需要访问的时候将它们载入到内存中。这样的好处就是节省内存,当web服务器产生故障时,还可以从文件系统或数据库中恢复Session的数据。管理session有两个类:1)StandardManager,这是一个默认的类,当tomcat启动或重载时将会session对象保存到指定文件中。2)PersistentManager,管理方式更加灵活,具有容错能力,可以及时把Session备份到Session Store中,可以控制内存中Session的数量。
# sql的优化相关问题
1. 对查询优化,避免全表扫描
2. 尽量避免where子句中对段进行null值判断,否则将导致引擎放弃使用索引而进行全表扫描。
3. 尽量避免where子句中出现!=或<>,否则将导致引擎放弃使用索引而进行全表扫描。
4. 尽量避免where子句中出现or来连接条件。
5. 慎用in和not in,否则导致全表扫描
6. where中不要用函数操作。
7. Update 语句,如果只更改1、2个字段,不要Update全部字段,否则频繁调用会引起明显的性能消耗,同时带来大量日志。
8. 对于多张大数据量(这里几百条就算大了)的表JOIN,要先分页再JOIN,否则逻辑读会很高,性能很差。
9. 尽可能的使用 varchar/nvarchar 代替 char/nchar,节省空间,提高查询效率
10. select count(*) from table;这样不带任何条件的count会引起全表扫描,并且没有任何业务意义,是一定要杜绝的。
# jvm 最大内存设置。设置的原理。结合垃圾回收讲讲
JVM内存可以分为堆内存和非堆内存,堆内存给开发人员用的,非堆内存给JVM本身用的,用来存放类型信息,即使GC时也不会释放空间。
堆内存设置:
-Xms 初始堆内存,默认物理内存1/64,也是最小分配堆内存,当空余堆内存小于40%时,会增加到-Xms的最大限制。
-Xmx 最大堆内存分配,默认物理内存1/4,当空余堆内存大于70%时,会减小打-Xms的最小限制。
非堆内存设置:
-XX:PermSize 非堆内存的初始值,默认物理内存的1/64,也是最小非堆内存。
-XX:MaxPermSize 非堆内存最大值,默认物理内存的1/4。
查看堆大小命令为Runtime.getRuntime().maxMemory()。
# Java是否有内存泄露和内存溢出
1、静态集合类
使用Set、Vector、HashMap等集合类的时候需要特别注意。当这些类被定义成静态的时候,由于他们的生命 周期跟应用程序一样长,这时候就有可能发生内存泄漏。
class StaticTest{
private static Vector v = new Vector(10);
public void init(){
for(int i = 1; i < 100; i++){
Object object = new Object();
v.add(object);
object = null;
}
}
}
在上面的代码中,循环申请了Object对象,并添加到Vector中,然后设置为null,可是这些对象呗vector引用着,因此不能 被GC回收,因此造成内存泄漏。因此要释放这些对象,还需要将它们从vector删除,简单的方法就是将vector设置为null
2、监听器
在Java编程中,我们都需要和监听器打交道,通常一个应用中会用到很多监听器,我们会调用一个控件,诸如 addXXXListener()等方法来增加监听器,但往往在释放的时候却没有去删除这些监听器,从而增加了内存泄漏的机会。
3、物理连接
一些物理连接,比如数据库连接和网络连接,除非其显式的关闭了连接,否则是不会自动被GC 回收的。Java 数 据库连接一般用DataSource.getConnection()来创建,当不再使用时必须用Close()方法来释放,因为这些连接是独立于 JVM的。对于Resultset 和Statement 对象可以不进行显式回收,但Connection 一定要显式回收,因为Connection 在任 何时候都无法自动回收,而Connection一旦回收,Resultset 和Statement 对象就会立即为NULL。但是如果使用连接池, 情况就不一样了,除了要显式地关闭连接,还必须显式地关闭Resultset Statement 对象(关闭其中一个,另外一个也会关 闭),否则就会造成大量的Statement 对象无法释放,从而引起内存泄漏。。一般情况下,在try代码块里创建连接,在 finally里释放连接,就能够避免此类内存泄漏。
4、内部类和外部模块等的引用
内部类的引用是比较容易遗忘的一种,而且一旦没释放可能导致一系列的后继类对象没有释 放。在调用外部模块的时候,也应该注意防止内存泄漏,如果模块A调用了外部模块B的一个方法,如: public void register(Object o) 这个方法有可能就使得A模块持有传入对象的引用,这时候需要查看B模块是否提供了出去引用的方法,这种情况容易忽略, 而且发生内存泄漏的话,还比较难察觉。
5、单例模式
因为单利对象初始化后将在JVM的整个生命周期内存在,如果它持有一个外部对象的(生命周期比较短)引用, 那么这个外部对象就不能被回收,从而导致内存泄漏。如果这个外部对象还持有其他对象的引用,那么内存泄漏更严重。
# 用什么工具可以查出内存泄漏
• MemoryAnalyzer:一个功能丰富的 JAVA 堆转储文件分析工具,可以帮助你发现内存漏洞和减少内存消耗
• EclipseMAT:是一款开源的JAVA内存分析软件,查找内存泄漏,能容易找到大块内存并验证谁在一直占用它,它是基于 Eclipse RCP(Rich Client Platform),可以下载RCP的独立版本或者Eclipse的插件
• JProbe:分析Java的内存泄漏。
# 现在有一个进程挂起了,如何用工具查出原因?
• 通过 Javacore 了解线程运行状况
javacore,也可以称为“threaddump”或是“javadump”,它是 Java 提供的一种诊断特性,能够提供一份可 读的当前运行的 JVM 中线程使用情况的快照。即在某个特定时刻,JVM 中有哪些线程在运行,每个线程执行到 哪一个类,哪一个方法。 应用程序如果出现不可恢复的错误或是内存泄露,就会自动触发 Javacore 的生成。而为了性能问题诊断的需要, 我们也会主动触发生成 Javacore。在 AIX、Linux、Solaris 环境中,我们通常使用 kill -3 <PID> 产生该进程的 Javacore。
# 垃圾回收算法使用的产品、场景
标记-清除算法:标记阶段,确定所有要回收的对象,并标记,清除阶段则将需要回收的对象清除。
复制算法:把内存分为大小相等的两块,每次使用其中的一块,当垃圾回收时,把存活的对象复制到另一块上,然后把这块内存整个清理掉。两块内存比是8:1
标记整理算法:把存活的对象往内存的一端移动,然后直接回收边界以外的内存。标记-整理算法提高了内存的利用率,并且它适合在收集对象存活时间较长的老年代。
分代回收算法:根据对象的存活时间把内存分为新生代和老年代,根据各代对象的存活特点,每代采用不同的GC算法。新生代用标记-复制算法,老年代用标记-整理算法。
视频讲解:https://www.bilibili.com/video/av47398444/
# 线程之间的通信
主要包括互斥锁、条件变量、读写锁和线程信号灯。
互斥锁:以排他方式防止数据被并发修改。互斥锁两个状态0和1。具体为申请锁、占用锁以防止数据被修改,此时默认阻塞等等,最后释放锁。
条件变量通信机制:原理,条件变量出现时,可以弥补互斥锁的缺陷,有些问题仅仅依靠互斥锁无法解决。但条件变量不能单独使用,必须配合互斥锁一起实现对资源的互斥访问。
读写锁:在对数据读写时,往往读占主要部分。基本原则是如果其他线程读数据,则允许其他线程执行读操作,但不允许写操作。如果有其他线程申请写操作,则其他线程不能申请读操作和写操作。
线程信号:线程拥有与信号相关的私有数据——线程信号掩码。线程可以向别的线程发送信号,每个线程可以设置自己的阻塞集合。所有线程中,同一信号子任何线程里的对该信号的处理一定相同。
# 什么是线程池?
java.util.concurrent.Executors提供了一个 java.util.concurrent.Executor接口的实现用于创建线程池
多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力。假设一个服务器完成一项任务所需时间为:T1 创建线程时间,T2 在线程中执行任务的时间,T3 销毁线程时间。
如果:T1 + T3 远大于 T2,则可以采用线程池,以提高服务器性能。
一个线程池包括以下四个基本组成部分:
1、线程池管理器(ThreadPool):用于创建并管理线程池,包括 创建线程池,销毁线程池,添加新任务;
2、工作线程(PoolWorker):线程池中线程,在没有任务时处于等待状态,可以循环的执行任务
3、任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等
4、任务队列(taskQueue):用于存放没有处理的任务。提供一种缓冲机制。线程池不仅调整T1,T3产生的时间段,而且它还显著减少了创建线程的数目,看一个例子:
假设一个服务器一天要处理50000个请求,并且每个请求需要一个单独的线程完成。在线程池中,线程数一般是固定的,所以产生线程总数不会超过线程池中线程的数目,而如果服务器不利用线程池来处理这些请求则线程总数为50000。一般线程池大小是远小于50000。所以利用线程池的服务器程序不会为了创建50000而在处理请求时浪费时间,从而提高效率。
# 常见线程池
①newSingleThreadExecutor
单个线程的线程池,即线程池中每次只有一个线程工作,单线程串行执行任务
②newFixedThreadExecutor(n)
固定数量的线程池,没提交一个任务就是一个线程,直到达到线程池的最大数量,然后后面进入等待队列直到前面的任务完成才继续执行
③newCacheThreadExecutor(推荐使用)
可缓存线程池,当线程池大小超过了处理任务所需的线程,那么就会回收部分空闲(一般是60秒无执行)的线程,当有任务来时,又智能的添加新线程来执行。
④newScheduleThreadExecutor
大小无限制的线程池,支持定时和周期性的执行线程
# JVM线程死锁,你该如何判断是因为什么?
如果用VisualVM,dump线程信息出来,会有哪些信息
• 常常需要在隔两分钟后再次收集一次thread dump,如果得到的输出相同,仍然是大量thread都在等待给同一个 地址上锁,那么肯定是死锁了。
# 黑盒测试、灰盒测试、白盒测试、单元测试有什么区别?
黑盒测试关注程序的功能是否正确,面向实际用户;
白盒测试关注程序源代码的内部逻辑结构是否正确,面向编程人员;
灰盒测试是介于白盒测试与黑盒测试之间的一种测试。
单元测试(Unit Testing)是对软件基本组成单元进行的测试,如函数或是一个类的方法。这里的单元,就是软件设计的最小单位。
# 什么是RMI?
答:Java远程方法调用(Java RMI)是Java API对远程过程调用(RPC)提供的面向对象的等价形式,支持直接传输序列化的Java对象和分布式垃圾回收。远程方法调用可以看做是激活远程正在运行的对象上的方法的步骤。RMI对调用者是位置透明的,因为调用者感觉方法是执行在本地运行的对象上的。https://www.cnblogs.com/xt0810/p/3640167.html
# 什么是Servlet?
答:Servlet 是用来处理客户端请求并产生动态网页内容的 Java 类。Servlet 主要是用来处理或者是存储 HTML 表单提交的数据,产生动态内容,在无状态的 HTTP 协议下管理状态信息。
# 说一下Servlet的体系结构
答:所有的 Servlet 都必须要实现的核心的接口是 javax.servlet.Servlet。每一个 Servlet 都必须要直接或者是间接实现这个接口,或者是继承 javax.servlet.GenericServlet 或者javax.servlet.http.HTTPServlet。最后,Servlet 使用多线程可以并行的为多个请求服务。
# Servlet的生命周期
主要分三个阶段:初始化——调用init()方法,响应客户请求阶段——调用service()方法,终止阶段——调用destroy方法。工作原理:客户发送一个请求,servlet调用service方法对请求进行响应,即对请求方式进行匹配,选择调用doGet、doPost方法等,然后进入对于的方法中调用逻辑层的方法,实现对客户的响应。自定义的servlet必须首先servlet接口。具体生命周期包括:装载Servlet、服务器创建Servlet实例、服务器调用Servlet的init()方法、客户请求到达服务器、服务器创建请求对象、服务创建相应对象、服务器激活Servlet的service方法,请求对象和响应对象作为service()方法的参数、service()方法获得关于请求对象的信息,处理请求,访问其他资源,获得需要的信息、service()方法可能激活其他方法以处理请求,如doGet(),doPost()
# EJB是基于哪些技术实现的?
问题:并说出SessionBean和EntityBean的区别,StatefulBean和StatelessBean的区别。
答:EJB包括Session Bean、Entity Bean、Message Driven Bean,基于JNDI、RMI、JAT等技术实现。
SessionBean在J2EE应用程序中被用来完成一些服务器端的业务操作,例如访问数据库、调用其他EJB组件。EntityBean被用来代表应用系统中用到的数据。
对于客户机,SessionBean是一种非持久性对象,它实现某些在服务器上运行的业务逻辑。
对于客户机,EntityBean是一种持久性对象,它代表一个存储在持久性存储器中的实体的对象视图,或是一个由现有企业应用程序实现的实体。
Session Bean 还可以再细分为 Stateful Session Bean 与 Stateless Session Bean ,这两种的 Session Bean都可以将系统逻辑放在 method之中执行,不同的是 Stateful Session Bean 可以记录呼叫者的状态,因此通常来说,一个使用者会有一个相对应的 Stateful Session Bean 的实体。Stateless Session Bean 虽然也是逻辑组件,但是他却不负责记录使用者状态,也就是说当使用者呼叫 Stateless Session Bean 的时候,EJB Container 并不会找寻特定的 Stateless Session Bean 的实体来执行这个 method。换言之,很可能数个使用者在执行某个 Stateless Session Bean 的 methods 时,会是同一个 Bean 的 Instance 在执行。从内存方面来看, Stateful Session Bean 与 Stateless Session Bean 比较, Stateful Session Bean 会消耗 J2EE Server 较多的内存,然而 Stateful Session Bean 的优势却在于他可以维持使用者的状态。
# 数据库中的范式有哪些?
1、 目前关系数据库有六种范式:
第一范式(1NF)
第二范式(2NF)
第三范式(3NF)
巴斯-科德范式 (BCNF)
第四范式(4NF)
第五范式(5NF,又称完美范式)。
满足低要求的范式是第一范式(1NF)。在 第一范式的基础上进一步满足更多规范要求的称为第二范式(2NF),其余范式以次类推。一般说来,数据库只需 满足第三范式(3NF)就行了。
2、 范式的包含关系
一个数据库设计如果符合第二范式,一定也符合第一范式。如果符合第三范式,一定也符合第 二范式…
3、范式
○ 1NF :符合1NF的关系中的每个属性都不可再分
○ 2NF:属性完全依赖于主键 [消除部分子函数依赖]
○ 3NF:属性不依赖于其它非主属性[消除传递依赖]
○ BCNF:在1NF基础上,任何非主属性不能对主键子集依赖[在3NF基础上消除对主码子集的依赖]
○ 4NF:要求把同一表内的多对多关系删除。
○ 5NF:从终结构重新建立原始结构。
#字符流和字节流
#是不是只有Java编译器才能完成java到class字节码的编译过程?
答:不是。Jpython/Scala/Groovy/JRuby都是可以编译成字节码文件的。
第二部分 API
一、数组
1、接口List的方法
boolean |
add(E e) 向列表的尾部添加指定的元素(可选操作)。 |
|
void |
add(int index, E element) 在列表的指定位置插入指定元素(可选操作)。 |
|
boolean |
addAll(Collection<? extends E> c) 添加指定 collection 中的所有元素到此列表的结尾,顺序是指定 collection 的迭代器返回这些元素的顺序(可选操作)。 |
|
boolean |
addAll(int index, Collection<? extends E> c) 将指定 collection 中的所有元素都插入到列表中的指定位置(可选操作)。 |
|
void |
clear() 从列表中移除所有元素(可选操作)。 |
|
boolean |
contains(Object o) 如果列表包含指定的元素,则返回 true。 |
|
boolean |
containsAll(Collection<?> c) 如果列表包含指定 collection 的所有元素,则返回 true。 |
|
boolean |
equals(Object o) 比较指定的对象与列表是否相等。 |
|
E |
get(int index) 返回列表中指定位置的元素。 |
|
int |
hashCode() 返回列表的哈希码值。 |
|
int |
indexOf(Object o) 返回此列表中第一次出现的指定元素的索引;如果此列表不包含该元素,则返回 -1。 |
|
boolean |
isEmpty() 如果列表不包含元素,则返回 true。 |
|
Iterator<E> |
iterator() 返回按适当顺序在列表的元素上进行迭代的迭代器。 |
|
int |
lastIndexOf(Object o) 返回此列表中最后出现的指定元素的索引;如果列表不包含此元素,则返回 -1。 |
|
ListIterator<E> |
listIterator() 返回此列表元素的列表迭代器(按适当顺序)。 |
|
ListIterator<E> |
listIterator(int index) 返回列表中元素的列表迭代器(按适当顺序),从列表的指定位置开始。 |
|
E |
remove(int index) 移除列表中指定位置的元素(可选操作)。 |
|
boolean |
remove(Object o) 从此列表中移除第一次出现的指定元素(如果存在)(可选操作)。 |
|
boolean |
removeAll(Collection<?> c) 从列表中移除指定 collection 中包含的其所有元素(可选操作)。 |
|
boolean |
retainAll(Collection<?> c) 仅在列表中保留指定 collection 中所包含的元素(可选操作)。 |
|
E |
set(int index, E element) 用指定元素替换列表中指定位置的元素(可选操作)。 |
|
int |
size() 返回列表中的元素数。 |
|
List<E> |
subList(int fromIndex, int toIndex) 返回列表中指定的 fromIndex(包括 )和 toIndex(不包括)之间的部分视图。 |
|
Object[] |
toArray() 返回按适当顺序包含列表中的所有元素的数组(从第一个元素到最后一个元素)。 |
|
|
toArray(T[] a) 返回按适当顺序(从第一个元素到最后一个元素)包含列表中所有元素的数组;返回数组的运行时类型是指定数组的运行时类型。 |
2、排序
List<Integer> l = new ArrayList<Integer>();
l.add(3);
l.add(1);
l.add(2);
l.add(9);
l.add(7);
Collections.sort(l);//默认排序(从小到大)
for(int i : l){
System.out.println(i);
}
Collections.reverse(l);//倒叙(从大到小)
for(int i : l){
System.out.println(i);
}
二、集合
1、Map 接口的方法
void |
clear() 从此映射中移除所有映射关系(可选操作)。 |
boolean |
containsKey(Object key) 如果此映射包含指定键的映射关系,则返回 true。 |
boolean |
containsValue(Object value) 如果此映射将一个或多个键映射到指定值,则返回 true。 |
Set<Map.Entry<K,V>> |
entrySet() 返回此映射中包含的映射关系的 Set 视图。 |
boolean |
equals(Object o) 比较指定的对象与此映射是否相等。 |
V |
get(Object key) 返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回 null 。 |
int |
hashCode() 返回此映射的哈希码值。 |
boolean |
isEmpty() 如果此映射未包含键-值映射关系,则返回 true。 |
Set<K> |
keySet() 返回此映射中包含的键的 Set 视图。 |
V |
put(K key, V value) 将指定的值与此映射中的指定键关联(可选操作)。 |
void |
putAll(Map<? extends K,? extends V> m) 从指定映射中将所有映射关系复制到此映射中(可选操作)。 |
V |
remove(Object key) 如果存在一个键的映射关系,则将其从此映射中移除(可选操作)。 |
int |
size() 返回此映射中的键-值映射关系数。 |
Collection<V> |
values() 返回此映射中包含的值的 Collection 视图。 |
Map<String, Integer> map = new HashMap<String, Integer>() {
{
put("I", 1);
put("V", 5);
put("X", 10);
put("L", 50);
put("C", 100);
put("D", 500);
put("M", 1000);
}
2、接口Set的方法
boolean |
add(E e) 如果 set 中尚未存在指定的元素,则添加此元素(可选操作)。 |
|
boolean |
addAll(Collection<? extends E> c) 如果 set 中没有指定 collection 中的所有元素,则将其添加到此 set 中(可选操作)。 |
|
void |
clear() 移除此 set 中的所有元素(可选操作)。 |
|
boolean |
contains(Object o) 如果 set 包含指定的元素,则返回 true。 |
|
boolean |
containsAll(Collection<?> c) 如果此 set 包含指定 collection 的所有元素,则返回 true。 |
|
boolean |
equals(Object o) 比较指定对象与此 set 的相等性。 |
|
int |
hashCode() 返回 set 的哈希码值。 |
|
boolean |
isEmpty() 如果 set 不包含元素,则返回 true。 |
|
Iterator<E> |
iterator() 返回在此 set 中的元素上进行迭代的迭代器。 |
|
boolean |
remove(Object o) 如果 set 中存在指定的元素,则将其移除(可选操作)。 |
|
boolean |
removeAll(Collection<?> c) 移除 set 中那些包含在指定 collection 中的元素(可选操作)。 |
|
boolean |
retainAll(Collection<?> c) 仅保留 set 中那些包含在指定 collection 中的元素(可选操作)。 |
|
int |
size() 返回 set 中的元素数(其容量)。 |
|
Object[] |
toArray() 返回一个包含 set 中所有元素的数组。 |
|
|
toArray(T[] a) 返回一个包含此 set 中所有元素的数组;返回数组的运行时类型是指定数组的类型。 |
3、Map<Integer,Integer> ans = new HashMap<>();
三、字符串
1、类String方法
char |
charAt(int index) 返回指定索引处的 char 值。 |
int |
codePointAt(int index) 返回指定索引处的字符(Unicode 代码点)。 |
int |
codePointBefore(int index) 返回指定索引之前的字符(Unicode 代码点)。 |
int |
codePointCount(int beginIndex, int endIndex) 返回此 String 的指定文本范围中的 Unicode 代码点数。 |
int |
compareTo(String anotherString) 按字典顺序比较两个字符串。 |
int |
compareToIgnoreCase(String str) 按字典顺序比较两个字符串,不考虑大小写。 |
String |
concat(String str) 将指定字符串连接到此字符串的结尾。 |
boolean |
contains(CharSequence s) 当且仅当此字符串包含指定的 char 值序列时,返回 true。 |
boolean |
contentEquals(CharSequence cs) 将此字符串与指定的 CharSequence 比较。 |
boolean |
contentEquals(StringBuffer sb) 将此字符串与指定的 StringBuffer 比较。 |
static String |
copyValueOf(char[] data) 返回指定数组中表示该字符序列的 String。 |
static String |
copyValueOf(char[] data, int offset, int count) 返回指定数组中表示该字符序列的 String。 |
boolean |
endsWith(String suffix) 测试此字符串是否以指定的后缀结束。 |
boolean |
equals(Object anObject) 将此字符串与指定的对象比较。 |
boolean |
equalsIgnoreCase(String anotherString) 将此 String 与另一个 String 比较,不考虑大小写。 |
static String |
format(Locale l, String format, Object... args) 使用指定的语言环境、格式字符串和参数返回一个格式化字符串。 |
static String |
format(String format, Object... args) 使用指定的格式字符串和参数返回一个格式化字符串。 |
byte[] |
getBytes() 使用平台的默认字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中。 |
byte[] |
getBytes(Charset charset) 使用给定的 charset 将此 String 编码到 byte 序列,并将结果存储到新的 byte 数组。 |
void |
getBytes(int srcBegin, int srcEnd, byte[] dst, int dstBegin) 已过时。 该方法无法将字符正确转换为字节。从 JDK 1.1 起,完成该转换的首选方法是通过 getBytes() 方法,该方法使用平台的默认字符集。 |
byte[] |
getBytes(String charsetName) 使用指定的字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中。 |
void |
getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) 将字符从此字符串复制到目标字符数组。 |
int |
hashCode() 返回此字符串的哈希码。 |
int |
indexOf(int ch) 返回指定字符在此字符串中第一次出现处的索引。 |
int |
indexOf(int ch, int fromIndex) 返回在此字符串中第一次出现指定字符处的索引,从指定的索引开始搜索。 |
int |
indexOf(String str) 返回指定子字符串在此字符串中第一次出现处的索引。 |
int |
indexOf(String str, int fromIndex) 返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始。 |
String |
intern() 返回字符串对象的规范化表示形式。 |
boolean |
isEmpty() 当且仅当 length() 为 0 时返回 true。 |
int |
lastIndexOf(int ch) 返回指定字符在此字符串中最后一次出现处的索引。 |
int |
lastIndexOf(int ch, int fromIndex) 返回指定字符在此字符串中最后一次出现处的索引,从指定的索引处开始进行反向搜索。 |
int |
lastIndexOf(String str) 返回指定子字符串在此字符串中最右边出现处的索引。 |
int |
lastIndexOf(String str, int fromIndex) 返回指定子字符串在此字符串中最后一次出现处的索引,从指定的索引开始反向搜索。 |
int |
length() 返回此字符串的长度。 |
boolean |
matches(String regex) 告知此字符串是否匹配给定的正则表达式。 |
int |
offsetByCodePoints(int index, int codePointOffset) 返回此 String 中从给定的 index 处偏移 codePointOffset 个代码点的索引。 |
boolean |
regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len) 测试两个字符串区域是否相等。 |
boolean |
regionMatches(int toffset, String other, int ooffset, int len) 测试两个字符串区域是否相等。 |
String |
replace(char oldChar, char newChar) 返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的。 |
String |
replace(CharSequence target, CharSequence replacement) 使用指定的字面值替换序列替换此字符串所有匹配字面值目标序列的子字符串。 |
String |
replaceAll(String regex, String replacement) 使用给定的 replacement 替换此字符串所有匹配给定的正则表达式的子字符串。 |
String |
replaceFirst(String regex, String replacement) 使用给定的 replacement 替换此字符串匹配给定的正则表达式的第一个子字符串。 |
String[] |
split(String regex) 根据给定正则表达式的匹配拆分此字符串。 |
String[] |
split(String regex, int limit) 根据匹配给定的正则表达式来拆分此字符串。 |
boolean |
startsWith(String prefix) 测试此字符串是否以指定的前缀开始。 |
boolean |
startsWith(String prefix, int toffset) 测试此字符串从指定索引开始的子字符串是否以指定前缀开始。 |
CharSequence |
subSequence(int beginIndex, int endIndex) 返回一个新的字符序列,它是此序列的一个子序列。 |
String |
substring(int beginIndex) 返回一个新的字符串,它是此字符串的一个子字符串。 |
String |
substring(int beginIndex, int endIndex) 返回一个新字符串,它是此字符串的一个子字符串。 |
char[] |
toCharArray() 将此字符串转换为一个新的字符数组。 |
String |
toLowerCase() 使用默认语言环境的规则将此 String 中的所有字符都转换为小写。 |
String |
toLowerCase(Locale locale) 使用给定 Locale 的规则将此 String 中的所有字符都转换为小写。 |
String |
toString() 返回此对象本身(它已经是一个字符串!)。 |
String |
toUpperCase() 使用默认语言环境的规则将此 String 中的所有字符都转换为大写。 |
String |
toUpperCase(Locale locale) 使用给定 Locale 的规则将此 String 中的所有字符都转换为大写。 |
String |
trim() 返回字符串的副本,忽略前导空白和尾部空白。 |
static String |
valueOf(boolean b) 返回 boolean 参数的字符串表示形式。 |
static String |
valueOf(char c) 返回 char 参数的字符串表示形式。 |
static String |
valueOf(char[] data) 返回 char 数组参数的字符串表示形式。 |
static String |
valueOf(char[] data, int offset, int count) 返回 char 数组参数的特定子数组的字符串表示形式。 |
static String |
valueOf(double d) 返回 double 参数的字符串表示形式。 |
static String |
valueOf(float f) 返回 float 参数的字符串表示形式。 |
static String |
valueOf(int i) 返回 int 参数的字符串表示形式。 |
static String |
valueOf(long l) 返回 long 参数的字符串表示形式。 |
static String |
valueOf(Object obj) 返回 Object 参数的字符串表示形式。 |
四、类Stack
类 Stack<E>
java.lang.Object
java.util.AbstractCollection<E>
java.util.AbstractList<E>
java.util.Vector<E>
java.util.Stack<E>
boolean |
empty() 测试堆栈是否为空。 |
E |
peek() 查看堆栈顶部的对象,但不从堆栈中移除它。 |
E |
pop() 移除堆栈顶部的对象,并作为此函数的值返回该对象。 |
E |
push(E item) 把项压入堆栈顶部。 |
int |
search(Object o) 返回对象在堆栈中的位置,以 1 为基数。 |
五、Tips
java刷题知识点总结:https://blog.csdn.net/yuzhengfei7/article/details/81903646
Integer.MAX_VALUE //最大值
Integer.MIN_VALUE //最小值
Ascii码表
'0'--------48
'A'--------65
'a'--------97
Stack<Character> stack = new Stack<>();//栈
List<List<Integer>> List = new ArrayList<>();//二维数组
Queue<Node> nodeQueue = new LinkedList<>();//一维队列链表
StringBuffer s = new StringBuffer("abc");
#三元运算符
C=A>B ? 100 :200;
这条语句的意思是,如果A>B的话,就将100赋给C,否则就将200赋给C;
将整数转成字符串: Integer.toString(100);
将整数转成对应进制的字符串: Integer.toString(a,2);
将纯数字字符串转成整数: Integer.valueOf("12345");
将纯数字字符串转成整数: Integer.valueOf("1100011",2);
将纯数字字符串转成整数: Integer.parseInt("12345");
将纯数字字符串转成整数: Integer.parseInt("1100011",2);
将一个整数转成二进制:Integer.toBinaryString(a);
将一个整数转成十六进制:Integer.toHexString(a);
将一个整数转成八进制:Integer.toOctalString(a);
题目:你总共有 n 枚硬币,你需要将它们摆成一个阶梯形状,第 k 行就必须正好有 k 枚硬币。给定一个数字 n,找出可形成完整阶梯行的总行数。n 是一个非负整数,并且在32位有符号整型的范围内。(注意类型转换)
class Solution {
public int arrangeCoins(int n) {
return (int) (Math.sqrt(2*(long)n+0.25) - 0.5);
}
}
1.对基本数据类型的数组的排序
说明:(1)Arrays类中的sort()使用的是“经过调优的快速排序法”;
(2)比如int[],double[],char[]等基数据类型的数组,Arrays类之只是提供了默认的升序排列,没提供相应的降序排列。
(3)要对基础类型的数组进行降序排序,需要将这些数组转化为对应的封装类数组,如Integer[],Double[],Character[]等,对这些类数组进行排序。(其实还不如先进行升序排序,自己在转为将序)。
用默认的升序对数组排序
函数原型:static void sort(int[] a)
对指定的 int 型数组按数字升序进行排序。
static void sort(int[] a, int fromIndex, int toIndex)
对指定 int 型数组的指定范围按数字升序进行排序。
代码实例:
import java.util.Arrays;
public class ArraysSort_11 {
public static void main(String args[])
{
int[] a={1,4,-1,5,0};
Arrays.sort(a);
//数组a[]的内容变为{-1,0,1,4,5}
for(int i=0;i<a.length;i++)
System.out.print(a[i]+" ");
}
}
public class Test extends Thread{
public static void main(String argv[]) {
Test b = new Test();
b.run();
}
public void start() {
for(int i=0;i<10;i++) {
System.out.println("Value of i ="+i);
}
}
}
运行结果:(无任何输出)
原因:本题中Test类继承了Thread,但没有重写Thread类的run()方法。因此,b.run()实际上调用的是Thread的run()方法,而Thread类的run()方法为空,因此这个程序能通过编译但是输出无结果。
public class Test{
public int test() {
int i; //错在这,没有初值
i++;
return i;
}
public static void mian(String args[]) {
Test test = new Test();
test.test();
System.out.print(new Test().test());
}
}
编译不通过
原因:变量在使用前必须初始化;
package test;
public class Test{
static int i;
public int test() {
i++;
return i;
}
public static void main(String args[]) {
Test test = new Test();
test.test();
System.out.print(new Test().test());
}
}
运行结果:2
原因:这题和上题有点区别,可能会觉得很奇怪,明明这题也没有初始化,为何这题可以输出结果。原因就在于static关键字修饰了i,默认初始化为0了。
public class Test{
public int f() {
return 1%5;
}
public static void main(String args[]) {
System.out.print(f()); //编译出错,因为非静态方法是对象的方法,不能脱离对象直接调用
}
}
////////////////////////////////////////////////////修改之后
public class Test{
public int f() {
return 1%5;
}
public static void main(String args[]) {
Test test = new Test();
System.out.print(test.f());
}
}
结果:(编译出错)
原因:静态方法内部只能调用静态方法,不能调用非静态方法。因为静态方法是类的方法,是不依赖于对象而存在的,在不创建对象的时候就可以调用,而非静态方法是对象的方法,只有对象被实例化后才存在。
public class Test{
public int f() {
static int i = 0; //编译出错,局部变量不能用static修饰
i++;
return i;
}
public static void main(String args[]) {
Test test = new Test();
test.f();
int j = test.f();
System.out.println(j);
}
}
结果:(编译不错)
原因:在java中,方法名称、成员变量都可以用关键字static修饰,但是局部变量不行,也就是说,方法体中的变量是不能被static修饰的。
第三部分 java学习路线
相关文章
- 暂无相关文章
用户点评