集合
1、获取字符串中每一个字母出现的次数。比如给定字符串”abcdabdeeadb”,输出结果:”a(3)b(3)c(1)d(3)e(2)”
法一:定义26个统计变量。遍历字符串,得到每一个字符进行判断,对应的统计变量++即可,输出拼接输出结果。但是很明显,造成浪费。
法二:
1、定义一个Map集合。
2、把字符串转换为字符数组。
3、遍历字符数组,得到每一个字符。
4、把这个字符到Map集合中查找看有没有这个字符存在,如果不存在,就把该字符作为键,1作为值存储;如果存在,就把值++,然后重
新存储该键和值。
5、定义一个字符串缓冲区
6、遍历TreeMap集合,获取每一个键值对元素拼接
7、把字符串缓冲区转换为字符串输出。
选择使用Map集合的哪一个类型呢?
可以发现输出是按照字母顺序排列的,所以我选择使用TreeMap,这样键会自动排序。
为什么要要用StringBuilder呢?
因为String对象是不可改变的。每次使用 System.String类中的方法之一时,都要在内存中创建一个新的字符串对象,这就需要为该新对象分配新的空间。在需要对字符串执行重复修改的情况下,与创建新的 String对象相关的系统开销可能会非常昂贵。如果要修改字符串而不创建新的对象,可以使用System.Text.StringBuilder类。例如,当在一个循环中将许多字符串连接在一起时,使用 StringBuilder类可以提升性能。
public class TestDemo {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入一个字符串:");
String str = scanner.nextLine();
//把字符串转换为字符数组
char[] charStr = str.toCharArray();
TreeMap<Character, Integer> tm = new TreeMap<Character, Integer>();
for(char ch:charStr){
//获取这个字符对应的值。通过返回值看这个字符是否存在
Integer i = tm.get(ch);
//如果不存在,存入集合,值设为1
if(i == null){
tm.put(ch, 1);
}else{
//如果存在,把这个键对应的值加1再存入集合中。
i++;
tm.put(ch, i);
}
}
//定义字符串缓冲区变量
StringBuilder sb = new StringBuilder();
//按照输出格式进行拼接
Set<Character> keySet = tm.keySet();
for(Character key:keySet){
Integer value = tm.get(key);
sb.append(key).append("(").append(value).append(")");
}
//把字符串缓冲区转换为字符串输出
String result = sb.toString();
System.out.println(result);
}
}
输出:
请输入一个字符串:
asbdakshdkgas
a(3)b(1)d(2)g(1)h(1)k(2)s(3)
2、HashMap和Hashtable的区别是什么?
Hashtable:线程安全,效率低。不允许null键和null值。
HashMap:线程不安全,效率高。允许null键和null值。
public class HashtableDemo {
public static void main(String[] args) {
HashMap<String, String> hm = new HashMap<String, String>();
hm.put("lili", "21");
hm.put("xiong", "22");
hm.put(null, null);
hm.put("caicai", "13");
System.out.println(hm);
System.out.println("------");
Hashtable<String, String> ht = new Hashtable<String, String>();
ht.put("lili", "21");
ht.put("xiong", "22");
//ht.put(null, null); //编译虽然不报错,但是运行就报错
ht.put("caicai", "13");
System.out.println(ht);
}
}
输出:
{null=null, lili=21, xiong=22, caicai=13}
------
{xiong=22, lili=21, caicai=13}
3、List,Set,Map等接口是否都继承自Map接口?
List、Set不是继承自Map接口,他们继承自Collection接口
Map接口本身就是一个顶层接口
4、Collection和Collections的区别是什么?
Collection:是单列集合的顶层接口。有子接口List和Set。
Collections:是针对集合操作的工具类。有对集合进行排序和二分查找的方法。
5、Map集合的四种遍历方式是哪四种?
可以去看我的这篇文章:http://blog.csdn.net/qq_36748278/article/details/77921523
6、TreeSet保证元素的唯一性和有序性的原理是什么?
可以去看我的这篇文章:http://blog.csdn.net/qq_36748278/article/details/77915801
7、HashSet保证元素的唯一性的原理是什么?
可以去看我的这篇文章:http://blog.csdn.net/qq_36748278/article/details/77842660
8、如何给ArrayList对象里面添加字符串?
可以去看我的这篇文章:http://blog.csdn.net/qq_36748278/article/details/76736235
实参与形参
java的基本数据类型是传值调用,对象引用类型是传引用。
当传值调用时,改变的是形参的值,并没有改变实参的值,实参的值可以传递给形参,但是,这个传递是单向的,形参不能传递回实参。因此在函数调用过程中,形参的值发生改变,而实参中的值不会变化。
当引用调用时,如果参数是对象,无论对对象做了何种操作,都不会改变实参对象的引用,但是如果改变了对象的内容,就会改变实参对象的内容。
代码1:
public static void testStringBuffer(StringBuffer sb){
sb.append("java");
}
public static void main(String[] args) {
StringBuffer sb = new StringBuffer("my ");
testStringBuffer(sb);
System.out.println("sb=" + sb.toString());
}
输出:sb=my java
对于这段代码,我们可以知道sb是个实参,它是通过new一个对象生成的,是一个对象引用,它指向的对象引用是”my “,因此它传递给testStringBuffer()方法中的参数也是一个引用,所以在方法内部对sb指向的引用中的数据进行的操作会反应在sb对象引用上,所以输出的是”sb=my java”
代码2:
public static String anaString(String str){
str = str + "_APPEND";
return str;
}
public static void main(String[] args) {
String s = "TEST";
anaString(s);
System.out.println("result = " + s);
s = anaString(s);
System.out.println("result_1 = " + s);
}
输出:
result = TEST
result_1 = TEST_APPEND
我们可以知道String是引用类型对象,参数传递的应该是地址值,调用了 testStr() 方法后,虽然在这个方法内部进行了值的拼接,但是由于 s 对象指向的引用没有变,所以 s 还是TEST,但是函数返回的就是进行拼接操作后的结果。
代码3:
public static void main(String [] args)
{
int a = 1;
int b = 2;
change(a,b);
System.out.println("a :" + a + ", b: "+b );
}
public static void change(int a ,int b)
{
int temp = 0;
temp = a;
a = b;
b = temp;
}
输出:a :1, b: 2
这种情况是参数是基本类型的数据,由于传值是单向的,实参的值可以传递给形参,但是形参的值不能传递回实参。所以实参的值还是不会改变。
代码4:
public static void main(String [] args)
{
ArrayList<String> list2 = new ArrayList<String>();
list2.add("AAAAA");
list2.add("BBBBB");
list2.add("CCCCC");
newList(list2);
System.out.println("sizeC=:"+list2.size());
}
public static void newList(ArrayList<String> list){
list=new ArrayList<String>();
list.add("DDDDD");
System.out.println("sizeB=:"+list.size());
}
输出:
sizeB=:1
sizeC=:3
因为这个虽然传递了一个引用数据类型的实参,但是在方法中重新new了一个新的对象,新的对象的值只添加了1个,所以sizeB输出的是1,但是对于实参list2来说,它指向的引用是没变的,它还是有3个值的,所以他的大小还是3。
也就是说改变了形参对象的引用,但是它的实参引用还是没有改变。
代码5:
public static void main(String [] args)
{
int [] a = new int[10];
int [] b = new int[10];
a[0] = 1;
b[0] = 2;
change(a,b);
System.out.println("a: " + a[0] + ",b: " + b[0] );
}
public static void change(int[] a ,int[] b)
{
int temp = 0;
temp = a[0];
a[0] = b[0];
b[0] = temp;
}
输出:
a: 2,b: 1
接口
Java接口的修饰符可以为()
A private B protected C final D abstract
答案:CD
解析:
(1)接口用于描述系统对外提供的所有服务,因此接口中的成员常量和方法都必须是公开(public)类型的,确保外部使用者能访问它们;
(2)接口仅仅描述系统能做什么,但不指明如何去做,所以接口中的方法都是抽象(abstract)方法;
(3)接口不涉及和任何具体实例相关的细节,因此接口没有构造方法,不能被实例化,没有实例变量,只有静态(static)变量;
(4)接口的中的变量是所有实现类共有的,既然共有,肯定是不变的东西,因为变化的东西也不能够算共有。所以变量是不可变(final)类型,也就是常量了。
接口的方法默认是public abstract;
所以接口的属性默认是public static final 常量,且必须赋初值。
JSP和Servlet
jsp的四大作用域是哪四个?
1、page:当前页面有效
2、request:请求中有效
3、session:在整个会话中有效
4、application:整个应用程序有效
从小到大:page < request < session < application
内部类
1、内部类
题目:要求在A、B、C位置填空分别输出30,20,10
class Outer{
public int num = 10;
class Inner{
public int num = 20;
public void show(){
int num = 30;
System.out.println(A);
System.out.println(B);
System.out.println(C);
}
}
}
public class Test {
public static void main(String[] args) {
Outer.Inner oi = new Outer().new Inner();
oi.show();
}
}
答案:
System.out.println(num);//30
System.out.println(this.num);//20
System.out.println(new Outer().num);//10
System.out.println(Outer.this.num);//10
分析:
1、声明的oi是一个Inner类的对象,也就是说this指代的是Inner类。所以要输出20,也就是Inner类的成员变量,可以通过this.num来获得
2、内部类和外部类没有继承关系。
3、可以通过外部类名限定this对象。
2、局部内部类访问局部变量的注意事项?
局部内部类访问局部变量必须用final修饰。
因为局部变量会随着方法的调用完毕而消失,但是这个时候,局部对象并没有立马从堆内存中消失,但还要使用那个变量。为了让数据还能继续被使用,就用fianl修饰。加了final修饰后,这个变量就变成了常量。既然是常量,你消失了,虽然变量名字不见了,但我在内存中存储的还是那个数据,还是有数据在使用。
final存储在堆内存中,堆内存的内容不会立即消失,只有垃圾回收机制回收的时候才会消失。
package org.danni.Demo2;
class Outer{
private int num = 10;
//局部内部类访问局部变量必须用final修饰
public void method(){
final int num2 = 20;
class Inner{
public void show(){
System.out.println(num);
System.out.println(num2);
}
}
//被垃圾回收机制回收的时候对象才消失。所以还会要用到num2变量。
//要还能够使用,使用final类型,存储在堆内存中,只有垃圾回收机制回首才消失。这样就可以了。
Inner i = new Inner();
i.show();
}
}
public class Test {
public static void main(String[] args) {
Outer o = new Outer();
o.method(); //10 20
}
}
3、匿名内部类
补齐代码,要求在控制台输出:”你很漂亮”
interface Inter{
void show();
}
class Outer{
}
public class Test {
public static void main(String[] args) {
Outer.method().show();
}
}
分析:
1、Outer.method()可以看出method()是Outer类的一个静态方法
2、Outer.method().show()可以看出method()方法有返回值,且返回的是一个对象。
3、由于接口Inter中有一个show方法,所以可以想得到method方法的返回值的类型是一个接口(本质是返回接口的子类实现对象),然后子类对象在调用它的show方法。
class Outer {
public static Inter method() {
return new Inter() {
@Override
public void show() {
System.out.println("你很漂亮");
}
};
}
}
基础
1、如果一个类没有构造方法,有哪些情况?
1、成员都是静态的,可以通过类直接调用。比如Math、Arrays、Collections
2、单例设计模式(Runtime)
3、类中有静态方法返回该类的对象。(InetAddress)
public static InetAddress getByName(String host),通过该类调用这个静态方法就会返回这个类的一个对象
2、写出下列程序输出的结果。
public class IntegerDemo {
public static void main(String[] args) {
Integer i1 = new Integer(127);
Integer i2 = new Integer(127);
System.out.println(i1 == i2); //false
System.out.println(i1.equals(i2)); //true
System.out.println("--------");
Integer i3 = new Integer(128);
Integer i4 = new Integer(128);
System.out.println(i3 == i4); //false
System.out.println(i3.equals(i4)); //true
System.out.println("--------");
Integer i5 = 128;
Integer i6 = 128;
System.out.println(i5 == i6); //false
System.out.println(i5.equals(i6)); //true
System.out.println("--------");
Integer i7 = 127;
Integer i8 = 127;
System.out.println(i7 == i8); //true
System.out.println(i7.equals(i8)); //true
}
}
Integer i7 = 127;把一个int类型的变量赋值给一个Integer引用类型的变量,在jdk5之后叫做自动装箱,这个编译器会帮我们完成,通过查看它的编译后的文件我们可以知道他其实经过了这样的操作:Integer i7 = Integer.valueOf(127);
下面我们查看valueOf方法的源码:我们可以发现针对在low=-128和high=127之间的数据创建了一个数据缓冲池。如果数据是该范围内的,就直接返回一个缓冲池中的值,否则就要重新new一个Integer对象。
因此代码中的i5,i6值为128超过了最大值127,因此通过new重新创建了一个空间,因此==操作返回的是false。
而i7,i8值为127,在范围内,就直接在缓冲池中取数据,并没有重新创建对象。所以==返回的是true
public static Integer valueOf(int i) {
assert IntegerCache.high >= 127;
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
}
private IntegerCache() {}
}
3、变量的值只有在运行的时候才能确定,而常量的值在编译期间就可以确定。
byte b1 = 3;
byte b2 = 4;
byte b;
//b = b1 + b2; //编译错误
b = 3 + 4; //常量,先计算,看计算结果是否在byte的范围内,如果存在,就不会报错
1、因为变量相加首先看类型问题,常量相加首先看结果看计算结果是否在赋值的数据类型范围内,如果是则正确,不是则报错。
2、因为b1和b2都是byte类型,他们在运行的时候才能计算它的结果,他们相加之后是int类型,而int类型不能转换为为byte类型(大可以转小,小不可以转大,int是da,byte是小),因此把一个int类型的值赋值给byte类型会报错
3、而3+4,都是常量,在编译的时候就计算好了,计算结果在byte范围内,就可以
4、throw和throws的区别是什么?
throws:
用在方法声明后面,跟的是异常类名
可以跟多个异常类名,用逗号隔开
表示抛出异常,由该方法的调用者来处理
throws表示出现异常的一种可能性,并不一定会发生这些异常
throw
用在方法体内,跟的是异常对象名
只能抛出一个异常对象名
表示抛出异常,由方法体内的语句处理
throw则是抛出了异常,执行throw则一定抛出了某种异常
5、final,finally和finalize的区别是什么?
final:可以修饰类、成员变量、成员方法
修饰类:类不能被继承
修饰变量:变量是常量
修饰方法:方法不能被重写
finally:是异常处理的一部分,用于释放资源。一般来说finally控制的代码肯定会执行,特殊情况:在执行到finally之前JVM退出了finally控制的代码就不会执行
finalize:是Object类的一个方法,用于垃圾回收。
6、如果catch里面有return语句,请问finally的代码还会执行吗?如果会,请问是在return前还是return后。?
会。在return前面执行。但是准确的说,是在return中间执行。
通过调试,我们可以发现程序是这么执行的:
public class FinallyDemo {
public static void main(String[] args) {
System.out.println(getInt()); //1 //9(控制台输出30)
}
public static int getInt(){
int a = 10; //2
try{
System.out.println(a / 0); //3(异常了,直接去执行catch)
a = 20;
}catch(ArithmeticException e){ //4
a = 30; //5
return a; //6 //8
//程序在执行到这一步的时候,这里不是return a而是return 30,这个返回路径就形成了。但是发现后面有finally,继续执行finally的内容,然后回到以前的返回路径,继续走return 30
}finally{
a = 40; //7(执行完之后,继续回到之前的返回路径)
}
return a;
}
}
但是如果也在finally之后加个return呢?这个时候输出的就是40了。
public class FinallyDemo {
public static void main(String[] args) {
System.out.println(getInt()); //1 //9(控制台输出40)
}
public static int getInt(){
int a = 10; //2
try{
System.out.println(a / 0); //3(异常了,直接去执行catch)
a = 20;
}catch(ArithmeticException e){ //4
a = 30; //5
return a; //6
}finally{
a = 40; //7
return a; //8
}
}
}
线程
1、同步有几种方式,分别是什么?
两种。同步代码块和同步方法。
2、sleep()和wait()方法的区别是什么?
1、sleep()方法必须指定时间。
wait()方法可以不指定时间,也可以指定时间。
2、sleep()不释放锁
wait()释放锁
3、为什么wait()、nitify()、notifyAll()等方法都定义在Object类中?
因为这些方法的对象的调用是依赖于锁对象的。而同步代码块的多对象是任意锁。而Object代码是任意的对象。所以,定义在Object里面。
4、线程的生命周期图
http://blog.csdn.net/qq_36748278/article/details/78144988