Java面试题——Java部分
文章目录
- Java面试题——Java部分
- 选择题
- 1.下面sum的值是( D )
- 2.下面程序的运行结果( A )
- 3.若x是`float`类型变量,`x=10/4;` 则`x`的值是( B )
- 4.以下程序的输出结果是(`x= 99 , x= 201`)
- 5.以下程序的输出结果是(`x= 200`)
- 6.以下程序的输出结果是(`200 300 400 500`)
- 7.以下程序的输出结果是(`x= 200`)
- 8.String str = new String(“abc”)创建了几个StringObject
- 9.String str = new String(“ab” +“c” + “d”)创建了几个String对象
- 10.下面程序的运行结果( C )
- 11.下面哪个关键字,使类不能再被其他类继承(C)
- 12.下面程序的运行结果( C )
- 13.下面程序的运行结果( C )
- 14.下面哪些是合法标识符( A 、B、E)
- 15.已知如下的命令执行 `java MyTest a b c`,请问哪个选项是正确的(C )
- 16.选项中哪个放到x处可保证程序正常编译( C )
- 17.下面程序的运行结果( D )
- 18.下面程序的运行结果( D )
- 19.请指出以下代码是否有错误及其控制台输出
- 20.下面程序的运行结果( A )
- 21.下面程序的运行结果( B )
- 22.下面程序在JDK1.8的环境下,描述正确的是( A 、D)
- 23.下面那些异常是检查异常,需要在编写程序时声明( C )
- 24.下面程序,当输入2时返回值是多少( D )
- 25.选项中哪一行代码可以替换题目中`//add code here` 而不产生编译错误( A )
- 26.下面程序在执行`new Child("mike")`的时候输出结果( D )
- 27.下面程序的运行结果
- 28.下面程序的运行结果( B )
- 29.下面程序的运行结果( B )
- 30.哪个语句创建了一个数组实例( A )
- 31.下面程序中插入哪行代码可以编译通过( B 、D)
- 32.下面程序中插入哪行代码可以编译通过(A)
- 33.编写一个Filter,需要(B)
- 34.哪一个对象可以用于获取浏览器发送的请求(A)
- 35.下面程序的运行结果( A )
- 36.以下语句`public class A{}`中,能合法定义的有(A、B、C)
- 37.父类SuperClass中定义了protected SuperClass map(int a,long b)方法,以下可以在子类中单独声明的方法有(A、C、D)
- 编程题
- 1.见题目
- 2.本地有一个存放学生的a.txt文件,内容如下
- 3.请写出对map集合的遍历实现(尽可能写出多种方式)
- 4.在Java中如何获得当前时间,如何转换为某字符串格式
- 5.对数组进行去重
- 6.对集合进行去重
- 7.请写出懒汉式和饿汉式单例模式
- 8.写一段代码删除list中所有结尾数字可被2整除的节点
- 9.如何对List根据age进行排序
- 简答题
- 1.列举几个常用的集合类并指出特点
- 2.Set里的元素是不能重复的,那么用什么方法来区分重复与否呢,是==还是equals(),有何区别?
- 3.请描述线程的生命状态,并描述sleep(),join(),yield()的区别以及interrupt()的作用
- 4.简述包装类中new方法和valueof方法的区别
- 5.内存溢出和内存泄漏
- 6.final、finally、finalize的区别是什么
- 7.Collection和Collections的区别
选择题
1.下面sum的值是( D )
int sum = 1;
for (int i = 0; i <10; i++){
i++; //1 3 5 7 9
sum += i; //1+1+3+5+7+9
}
System.out.println(sum);
A.46 B.24 C.45 D.26
2.下面程序的运行结果( A )
Integer a = 300; //自动装箱 a = new Integer(300)
Integer b = 300; //自动装箱 b = new Integer(300)
int c = 300 ;
System.out.println(a == b); // false
System.out.println(a == c); // true == 两侧为引用类型和基本数据类型,将自动拆箱,进行数值比较
A.false ; true
B.false ; false
C.true ; false
D.true ; true
自动拆装箱操作:从JDK1.5开始提供,自动拆箱是在编译期完成,自动拆装箱底层都是通过调用方法来完成
- xx.java: Integer a = 100; int x = a;
- xx.class: Integer a = Integer.valueOf(100); int x = a.intValue();
valueOf()
:Integer底层实现是:判断整数的值,是否处于-128~127之间,若处于该区间,则返回常量池中的Integer对象,不会新建,若整数不处于该区间,此时底层 new Integer(i),为新创建的对象基本数据类型的包包装类中 所有的整数都有常量池,小数(浮点型)、布尔是没有常量池的
3.若x是float
类型变量,x=10/4;
则x
的值是( B )
A. 2 B. 2.0 C. 2.5 D. 编译错误
4.以下程序的输出结果是(x= 99 , x= 201
)
public class HasStatic() {
private int x = 100;
public static void main(String[] args) {
HasStatic has1 = new HasStatic();
has1.x++;
HasStatic has2 = new HasStatic();
has2.x += has1.x;
has1 = new HasStatic();
has1.x--;
System.out.println("x=" + has1.x); //99
System.out.println("x=" + has2.x); //201
}
}
//考点: 实例变量是属于实例的,会随着实例的重新创建而重新初始化
5.以下程序的输出结果是(x= 200
)
public class HasStatic() {
private static int x = 100;
public static void main(String[] args) {
HasStatic has1 = new HasStatic();
has1.x++;
HasStatic has2 = new HasStatic();
has2.x += has1.x;
has1 = new HasStatic();
has1.x--;
HasStatic.x--;
System.out.println("x=" + has1.x); //200
}
}
//考点: static变量是属于类的,在类加载时期完成初始化,且只初始化一次
6.以下程序的输出结果是(200 300 400 500
)
public class Test{
static{
System.out.println("200");
}
public Test(){
System.out.println("500");
}
{
System.out.println("300");
}
public static void main(String[] args) {
new Test();
}
{
System.out.println("400");
}
}
// static代码块 -> 代码块(构造块) -> 构造方法
7.以下程序的输出结果是(x= 200
)
String s1 = "abc", s2 = "def", s3 = "abcdef";
String s4 = s1 + s2;
String s5 = "abc" + "def";
String s6 = new String("abc");
System.out.println(s1 == s6); //false
System.out.println(s3 == s4); //false
System.out.println(s3 == s5); //true
//1.字符串变量之间的拼接在运行期完成,通过StringBuilder进行拼接,StringBuilder的toString()方法底层是new String
//2.字符串字面量之间的拼接在编译期完成
8.String str = new String(“abc”)创建了几个StringObject
//2个 一个是 new String, 另一个是 字面量 “abc”
9.String str = new String(“ab” +“c” + “d”)创建了几个String对象
//2个 一个是 new String, 另一个是 字面量 “abcd” 是因为字符串字面量之间的拼接是在编译期完成的
//在 .class文件中代码为 String str = new String("abcd")
10.下面程序的运行结果( C )
abstract class MineBase {
abstract void amethod();
static int i;
}
public class Mine extends MineBase {
public static void main(String[] args) {
int[] ar = new int[5];
for (i = 0; i < ar.length; i++) {
System.out.println(ar[i]);
}
}
}
A. 打印5个0
B. 编译出错,数组ar[]必须初始化
C. 编译出错,Mine应声明为abstract
D. 出现IndexOutOfBoundsException异常
11.下面哪个关键字,使类不能再被其他类继承(C)
A.static B.finally C.final D.instance
final
:是一个修饰符
- 类:类不能被继承
- 方法:方法不能被重写
- 变量:可以是成员变量也可以是局部变量,变量值一旦初始化不可改变
- 修饰成员变量:必须在声明同时初始化
- 修饰局部变量:可以先声明再初始化
static
:是一个修饰符
- 成员变量:在类加载时期完成初始化,且只初始化一次,通过类名调用,也可通过对象调用不过不推荐
- 方法:通过类名调用静态方法,static方法内部不能使用
this
、super
关键字(静态方法中是通过类名来调用方法的,不存在通过实例来调用,this
是引用,指向调用当前方法的对象,super
指向的是父类对象,所以都不存在,而非静态方法默认存在this
、super
关键字)- 代码块:在类加载时期执行结束,且只执行一次
注:static可以修饰类,但必须是内部类,如
LinkedList
的内部类Node
,其本质是修饰类中的成员,内部类就是类中的成员
finally
:和try...catch
配合使用,finally
块中的代码不论之前是否有异常出现,都会执行,通常在finally
块中保存释放资源的代码
instanceof
:判断某个实例是否属于某种类型eg:
if(per instanceof Person)
12.下面程序的运行结果( C )
public class Ppvg{
public static void main(String[] args) {
Ppvg p = new Ppvg();
int x = p.flition();
System.out.println(x);
}
public int flition(){
try{
FileInputStream din = new FileInputStream("Ppvg.java");
din.read();
}catch(IOException e){
System.out.println("flytwick");
return 99;
}finally{
System.out.println("flition");
}
return -1;
}
}
A. 程序正常运行并输出: "flition" 和 99
B. 程序正常运行并输出: "flytwick"、"flition" 和 -1
C. 程序正常运行并输出: "flytwick"、"flition" 和 99
D. 在编译时产生错误,因为flition方法要求返回两个值
//以上代码执行流程
/*
9行 执行时会报异常,若通过输入流直接写入文件名称,可能出现找不到,因为直接找的是当前项目下,而类存在于src下
抛出异常后被 11行 捕获异常 进入 catch块 输出 12行内容 "flytwick"
如果 try...catch块中有 finally ,finally必执行,则会在 catch块返回时 先进入 finally块 输出 15行内容 "flition"
最后返回 catch块 执行 13行 返回 99 程序结束
所以最终输出 "flytwick"、"flition" 和 99
*/
13.下面程序的运行结果( C )
public class Ppvg{
public static void main(String[] args) {
Ppvg p = new Ppvg();
int x = p.flition();
System.out.println(x);
}
public int flition(){
int i = 99;
try{
FileInputStream din = new FileInputStream("Ppvg.java");
din.read();
}catch(IOException e){
System.out.println("flytwick");
return i;
}finally{
i++;
System.out.println("flition");
}
return -1;
}
}
A. 程序正常运行并输出: "flition" 和 99
B. 程序正常运行并输出: "flytwick"、"flition" 和 100
C. 程序正常运行并输出: "flytwick"、"flition" 和 99
D. 在编译时产生错误,因为flition方法要求返回两个值
//以上代码执行流程
/*
10行 执行时会报异常,若通过输入流直接写入文件名称,可能出现找不到,因为直接找的是当前项目下,而类存在于src下
抛出异常后被 12行 捕获异常 进入 catch块 输出 13行内容 "flytwick" ,此时 14行 返回时 i=99
如果 try...catch块中有 finally ,finally必执行,则会在 catch块返回时 先进入 finally块 执行 16行 i=100 ,输出 17行内容 "flition"
最后返回 catch块 执行 14行,此时到底 返回100还是99 答案是返回 99 程序结束
其涉及内部操作,当catch一开始要返回时,因为finally中的代码必须执行,此时catch中的return就会暂停,但其实是知道要返回的值是多少,相当于在这里会拍一个快照,在之后执行任何操作都不会影响这个快照,之后返回的值还是这个快照中的值,而finally修改的是方法里的值,返回的是之前记录的值,所以finally中的操作属于附带操作
所以最终输出 "flytwick"、"flition" 和 99
*/
14.下面哪些是合法标识符( A 、B、E)
A. $persons
B. TwoUsers
C. *point
D. this
E. _ending
F. 2_4dis
//所以 标识符 是由 数字、字母、_、$ 组成,且数字不能开头,且不能使用Java已经定义好的关键字
15.已知如下的命令执行 java MyTest a b c
,请问哪个选项是正确的(C )
A. args[0]="MyTest a b c"
B. args[0]="MyTest"
C. args[0]="a"
D. args[1]="b c"
关于 java命令:在jdk目录下bin文件夹下的可执行文件 就是一条条命令
搭建Java环境:安装JDK和配置JDK环境变量
JAVA_HOME
: JDK的安装路径
path
: bin目录的路径
classpath
:. 类库
java
命令:关于运行,运行字节码文件的名称
java class_name
--对某个类运行,执行其main方法
javac
命令 关于编译,指定java源文件进行编译产生.class文件
javac xx.java
--对源文件进行遍历如果想传参 只需 在多个参数间 空格 即可
16.选项中哪个放到x处可保证程序正常编译( C )
interface Foo{
int bar();
}
public class Sprite{
public int fubar(Foo foo){
return foo.bar();
}
public void testFoo(){
fubar();//此处为X
}
}
A. Foo{ public int bar({return 1;}}
B. new Foo{public int bar({return 1;}}
C. new Foo(){public int bar({return 1;}}
D. new class Foo{public int bar(ireturn 1;}}
//接口 中的方法是抽象方法
//匿名内部类
new Foo(){ //创建匿名内部类对象
//重写抽象方法
bar(){
//...
}
}
17.下面程序的运行结果( D )
class A{
void foo() throw Exception{
throw new Exception();
}
}
class SubA extends A{
void foo(){
System.out.println("A");
}
}
class Tester{
public static void main(String[] args) {
A a = new SubA();
a.foo();
}
}
A. A
B. A ,抛出Exception
C. 编译失败,7行出现错误
D. 编译失败,14行出现错误
E. 无输出,但抛出Exception
重写的规则:2同2小1大
- 2同:方法名相同,参数列表相同
- 2小:子类方法返回值类型小于等于父类。子类方法抛出的异常小于等于父类(抛出异常数量,子类方法抛出异常类型可以是父类抛出异常类型的子类)
- 1大:子类重写方法的访问控制修饰符必须大于等于父类
通过父类类型的引用调用重写的方法
- 运行期:一定调用的是重写后的方法,即是子类的
- 编译期:调用到的是数据类型所属类的方法,看的是左侧的类型,即调用的方法是父类中的方法
所以本题中 14行 需要捕获或抛出异常
18.下面程序的运行结果( D )
static void test() throws RuntimeException{
try{
System.out.print("test");
throw new RuntimeException();
}catch(Exception e){
System.out.print("exception"); //这里已经捕获了异常
}
}
public static void main(String[] args) {
try{
test();
}catch(RuntimeException ex){
System.out.print("runtime"); //已经没有异常要捕获了
}
System.out.print("end");
}
A. test end
B.编译失败
C. test runtime end
D. test exception end
19.请指出以下代码是否有错误及其控制台输出
public class test{
private static Integer b; //null
public static void main(String[] args) {
int a = 10;
print(a);
print(b); //编译期无异常,运行时异常 此时会报空指针异常
b = a;
print(b);
}
private static void print(Integer value){
int add = value + 5; //value.intValue()
System.out.println("add value = " + add);
}
}
//最终 先输出 add value = 15,然后抛出 NullPointerException
20.下面程序的运行结果( A )
public class A implements B{
public static void main(String[] args) {
int i;
A c1 = new A();
i = c1.K; //正常通过类名、接口名访问;通过实例访问静态常量可以,不过不建议
System.out.print("i = " + i);
}
}
interface B{
int K = 10;
}
A. i = 10
B.编译失败
21.下面程序的运行结果( B )
public static void main(String[] args) {
Thread t = new Thread(){
public void run(){
pong();
}
}
t.run(); //并不是启动线程,直接调用run() 只是个普通方法,所以只有一个线程
System.out.print("ping");
}
static void pong(){
System.out.print("pong");
}
A. pingpong
B.pongping
C. pingpong 和 pongping 都有可能
D. 都不输出
//start()和run()
// start()表示启动线程
// 直接调用run()方法只是普通方法的调用,线程并没有启动
22.下面程序在JDK1.8的环境下,描述正确的是( A 、D)
interface Er{
double PI = 3.14;
void tongdian();
public default void duandian(){
}
}
interface Ar{
public default void duandian(){
}
}
class Dfqc implements Er,Ar{
public void tongdian(){
System.out.print("通电函数" + PI);
}
public class Test{
public static void main(String[] args) {
Dfqc t = new Dfqc();
t.tongdian();
t.duandian();
}
}
}
A.在类Dfqc中,没有实现duandian()方法,编译不通过
B.在接口中定义的PI没有用final修饰,不是常量,可以更改数值
C.编译正确,duandian()方法是接口中定义的普通方法,在JDK1.8后允许,实现类对象可以直接调用
D.编译错误,duandian()方法在卜接口中均存在,类Dfqc实现了两个接口,该类中必须重写duandian()编译才正确
/*
JDK1.8的新特性
1. lambda表达式
2. 新日期 LocalDateTime.now()
3. 接口中可以定义默认方法和static方法
4. :: 方法引用
lambda表达式作用: 为简化代码而出现 ()-> {}
1.实现功能性接口(函数式接口)
功能性/函数式接口: 接口中只有一个抽象方法,可以有多个静态方法,默认方法且在接口上方有注解@FunctionalInterface
lambda表达式各部分代表的意义:
(): 重写方法的参数列表
{}: 重写方法的方法体
lambda表达式各部分的简化:
(): 参数列表中的数据类型都可以省略;若参数列表中只有一个参数,此时()可以省略,若参数列表无参或有多个参数,此时()都不可以省略
{}: 若体部分只有一行代码,此时{}可以省略,若体部分只有一行代码且有return,此时return也可以省略
2.对集合进行遍历
集合若想使用lambda表达式遍历,需要集合对lambda的支持,支持是指:看集合中是否提供forEach(),若有则可以使用lambda对集合进行遍历
list.forEach(str->System.out.println(str));
set.forEach(str->System.out.println(str));
map.forEach((k, v)->System.out.println(k+"-"+v));
新日期 LocalDateTime.now()
LocalDateTime time = LocalDateTime.now();
System.out.println(time);
旧日期 Date date = new Date();
接口中可以定义默认方法和static方法
JDK1.8之前,接口中有的成员只有:
常量(接口为其提供了默认修饰符) public static final
抽象方法(接口为其提供了默认修饰符) public abstract
JDK1.8开始,允许在接口中定义默认方法和static方法:
默认方法需要添加修饰符 default,通过接口的实现类对象来调用方法
若一个实现类实现了两个接口,且两个接口中有相同的默认方法,此时在实现类汇总必须对该方法进行重写,否则编译不通过
静态方法 和平时的static方法定义是相同的,通过接口名调用static方法即可
:: 方法引用: 用于简化lambda表达式的,不做其他
简化的前提:lambda表达式中的参数会作为参数传递给体中调用的方法,且是所有参数都传递给调用的方法,此时使用 :: 来简化lambda表达式
(a,b,c)->{A.test(a,b,c)} ==》 A::test
1. static方法引用
1.定义功能性接口
2.出现接口作为参数出现
3.使用::简写lambda表达式
2. 实例方法引用
3. 构造方法引用
4. 数组的构造方法引用
*/
23.下面那些异常是检查异常,需要在编写程序时声明( C )
A.NullPointerException
B.ClassCastException
C.FileNotFoundException
D.IndexOutOfBoundsException
//所有异常的父类 Throwable,其子类有 Exception、Error
/*
Error:
栈内存溢出异常: StackOverFlowError
递归没有出口、递归深度太深
堆内存溢出异常: OutOfMemoryError
创建的对象太大、内存泄漏的一直累积
Exception:
检查异常: 在编译过程中可检测到的,且必须处理的异常,若不处理编译不通过
文件找不到异常: FileNotFoundException
IO异常: IOException
中断异常: InterruptedException
解析异常: ParseException
类找不到异常: ClassNotFoundException
特点:所有的检查异常一定是在方法声明位置通过throws来抛出了异常类型
运行时异常: 在编译期检测不到,是在运行期出现的异常
空指针异常: NullPointerException
类转换异常: ClassCastException
下标越界异常: IndexOutOfBoundsException
数字转换异常: NumberFormatException
throws 和 throw 的区别:用法不一样
throws: 作用于方法声明位置,抛出的是异常类型
throw: 作用于方法体中,抛出的是异常对象
*/
24.下面程序,当输入2时返回值是多少( D )
public static int getValue(int i){
int result = 0;
switch(i){
case 1:
result = result + i;
case 2:
result = result + i * 2; //分支中没有break; 则继续向下执行
case 3:
result = result + i * 3;
return result;
}
}
A. 2 B. 0 C. 4 D. 10
25.选项中哪一行代码可以替换题目中//add code here
而不产生编译错误( A )
public abstract class Myclass{
public int constInt = 5;
//add code here
public void method(){
}
}
A.public abstract void method(int a); //重载
B.constInt = constInt + 5; //执行代码不能直接写在类的成员位置,必须存在{}中
C.public int method(); //抽象方法没有abstract; 和类中的method方法签名完全一致,不能存在同一个类中
D.public abstract void anotherMethod(){} //抽象方法不能有方法体
- 重写:是出现在父子类中
- 重载:是指在同一个类中出现的方法名相同,参数列表不同的方法,重载和方法的返回/访问控制修饰符都无关
26.下面程序在执行new Child("mike")
的时候输出结果( D )
class People(){
String name;
public People(){ //这里隐藏了super();
System.out.print(1);
}
public People(String name){ //这里隐藏了super();
System.out.print(2);
this.name = name;
}
}
class Child extends People(){
People father;
public Child(String name){ //这里隐藏了super();
System.out.print(3);
this.name = name;
father = new People(name + ":F");
}
public Child(){ //这里隐藏了super();
System.out.print(4);
}
}
A. 312 B. 32 C. 432 D. 132
//构造方法的第一行代码永远都是super()
27.下面程序的运行结果
public class SubCls extends SuperCls{
int a = 6;
public SubCls(){
test();
}
public void test(){
System.out.println(a);
}
public static void main(String[] args){
new SubCls();
}
}
class SuperCls{
int a = 8;
public SuperCls(){
test();
}
public void test(){
System.out.println(a);
}
}
//运行结果: 0 6
/*
成员变量不进行初始化的话也会有默认初始值,在刚创建好对象时就赋有初始值
人为初始化后,仍会有默认初始值
关于成员变量人为初始化的时机,在构造方法里面进行初始化
在运行期时,若一个方法在父子类中都存在,到底调用谁,看运行期创建的对象
若父子类中都存在一个相同的变量,值不同,则子类访问子类的,父类访问父类的
*/
28.下面程序的运行结果( B )
class Base{
Base(){
System.out.println("Base");
}
}
public class Alpha extends Base{
public static void main(String[] args){
new Alpha();
new Base();
}
}
A.Base
B.Base Base
C. 编译失败
D.代码运行但没输出
E. 运行时抛出异常
//子类构造方法中第一行是 super()
29.下面程序的运行结果( B )
public class Test1{
public static void main(String[] args){
System.out.println("返回值为:" + new Test1().test());
}
public int test(){
int i = 1;
try{
System.out.println("try中的i值为:" + i); // 1
return i; // 1 返回快照 1
}finally{
++i; // 2
System.out.println("finally中的i值为:" + i); // 2
}
}
}
A. 1 1
B. 1 2 1
C. 1 2 2
D. 1 2
30.哪个语句创建了一个数组实例( A )
A.int[] i = new int[15];
B.float f = new float[20]; //左边是变量,右边是对象 类型不匹配
C. char[] c = "Some String"; //左边是字符数组,右边是字符串 类型不匹配
D.int a[][] = {3,12,15}{1123,53,5}; //二维数组
//数组中的元素类型可以是任意的(机泵数据类型,引用类型),而数组本身就是引用类型,若数组中的元素为数组类型,则出现了二维数组/多维数组
/*
关于二维数组:
定义:
初始化方式1:直接赋值
int[][] ary = {{1,3},{4,25,1},{123,5,12,54}};
初始化方式2:若二维数组的长度一致,此时可以采用以下方法
int[][] ary = new int[4][2];
访问:
访问某个位置的元素,通过下标访问 arr[1][1]
遍历:
for循环遍历,for循环里嵌套for循环
*/
31.下面程序中插入哪行代码可以编译通过( B 、D)
class Test{
public static void main(Stirng arg){
doSomething(1);
doSomething(1,2);
}
//插入代码处
}
A.static void doSomething(int[] args){}
B.static void doSomething(int...args){}
C. static void doSomething(int...args,int x){}
D.static void doSomething(int x,int...args){}
E. static void doSomething(int...arg1,int...arg2){}
//关于可变参数: 个数可变的参数
/*
格式: int...is
可变参数的访问:
1.其底层为数组,所以访问方式和数组相同
2.因为可变参数的底层是数组,所以可以接受数组
3.但反过来如果方法参数列表是数组,只能传递相同数据类型的值,即接受的值一定是数组,不能是其他类型
4.可变参数若出现在参数列表中,则必须是参数列表的最后一个参数
5.参数列表中的可变参数最多只能有一个,不管是不是同一数据类型(一刀切)
*/
32.下面程序中插入哪行代码可以编译通过(A)
public class A{
public static void main(Stirng arg){
List list = new ArrayList();
Teacher a = new Teacher();
a.name = "王五";
a.age = 30;
list.add(a);
a.name = "张三";
a.age = 40;
list.add(a);
Teacher b = (Teacher) list.get(0);
b.name = "李四";
b.age = 50;
list.add(b);
System.out.println(list.get(0)); //集合中保存的是对象的引用
System.out.println(list.get(1));
}
static class Teacher{
public String name;
public int age;
public String toString(){
return name + ":" + age;
}
}
}
A: 李四:50 李四:50
B: 王五:30 李四:50
C: 王五:30 张三:40
D: 张三:40 李四:50
33.编写一个Filter,需要(B)
A: 继承Filter类
B: 实现Filter接口 //含抽象方法 doFilter() 过滤功能写在这个方法中
C: 继承HttpFilter类
D: 实现HttpFilter接口
34.哪一个对象可以用于获取浏览器发送的请求(A)
A: HttpServletRequest
B: HttpServletResponse
C: HttpServlet
D: Http
35.下面程序的运行结果( A )
String a = "AAA";
a.toLowerCase();
System.out.println(a);
A. aaa B. AAA C. a D. A
36.以下语句public class A{}
中,能合法定义的有(A、B、C)
A: public class innerClass{}
B: public static class nestedClass{}
C: public interface nestedInterface{}
D: public static Iterator<A> iterator =
37.父类SuperClass中定义了protected SuperClass map(int a,long b)方法,以下可以在子类中单独声明的方法有(A、C、D)
A: public SuperClass map(int a,long b)
B: private SuperClass map(int a,long b)
C: public SuperClass map(int a,int b)
D: protected ChildClass map(int a,long b)
E: public long map(int a,long b)
- 重写:2同2小1大
- 2同:方法名相同,参数列表相同
- 2小:子类抛出异常类型 <= 父类方法抛出异常;子类返回值类型 <= 父类方法返回值类型
- 1大:子类方法访问控制修饰符 >= 父类方法访问控制修饰符
- 重载:方法名相同,参数列表不同;和方法的其他部分无关
编程题
1.见题目
- 将
count
文件用程序读取,并打印在控制台上- 将
count
内的每个编码出现的次数统计出来
- 有多少个
- 每个出现的次数
- 将结果通过
text
或excel
输出到文件
B7151890053952,B7151890053952,B7151890053952,B715189005391880590647,39101880612299,39101880612299,39101880612299,39621353,39101880621353,39101880621353,39101880621353,39101835,B7151890054335,39101880621376,39101880621439,B7151890057151890054598,39101880613720,39101880590256,3910188062105880621288,39101880621058,39101880621117,39101880621388,39121388,39101880621058,B7151890054697,39101880621117,39101889,39101880621388,39101880621058,B7151890054697,39101880621101880590102,39101880590102,39101880590102,B7151890053952
public class Demo01{
public static void main(String[] args) throws IOException{
//1.将count文件用程序读取,并打印在控制台上
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("count源文件.txt")));
String line = null;
while((line = br.readLine()) != null){
System.out.println(line);
}
br.close();
//2.将count内的每个编码出现的次数统计出来 有多少个 每个出现的次数 并将结果写入文件
/*
思路:对文件按照单个字符进行读取,判断读取到的字符是否为 "," 若不是,将其存入StringBuilder,若是将StringBuilder中的内容转换成字符串,即为编码,将编码存入map集合,存的时候判断该编码是否已经存在,若存在,让其该编码的次数进行 +1,若不存在,将该编码存入map,次数设为1
*/
PrintWriter pw = new PrintWriter(new FileOutputStream("result.txt"),true);
Map<String,Integer> map = new HashMap()<>;
StringBuilder sb = new StringBuilder();
//按照单个字符读取数据
InputStreamReader isr = new InputStreamReader(new FileInputStream("count源文件.txt"));
int ch = -1;
while((ch = isr.read()) != -1){
//判断字符是否为 ,
if(ch != ','){
sb.append((char)ch);
}else{
String code = sb.toString();
//判断 code 是否存在于 map中
if(map.containsKey(code)){
map.put(code,map.get(code) + 1);
}else{
map.put(code,1);
}
//清空 StringBuilder
sb.delete(0,sb.length()); //包头不包尾
}
}
String count = "编码的个数为:" + map.size();
System.out.println(count);
pw.println(count);
map.forEach((k,v)->{
String line = "编码:" + k + "出现的次数:" + v;
System.out.println(line);
pw.println(line);
});
isr.close();
pw.close();
}
}
关于IO流:
- 按照流向分:输入流、输出流
- 按照读写单位分:字节流、字符流
- 低级流和高级流:低级流直接面向文件操作,高级流直接面向流
字节流:若操作文件为非文本文件(图片、音频、视频),此时必须选择字节流进行读写
抽象基类:(都是抽象类,不能实例化,只能创建子类对象)
InputStream
OutputStream
文件流:(输入流是读数据,输出流是写数据,参照物不同以内存为标准,从内存输出到文件是写,从文件输入到内存是读)
FileInputStream
构造方法:
FileInputStream(File file){} //通过打开一个到实际文件的连接来创建一个FieInputStream,该文件通过文件系统中的File对象file指定。 FileInputStream(String name){} //通过打开一个到实际文件的连接来创建一个FileInputStream,该文件通过文件系统中的路径名name指定。
读数据:
read():int //从此输入流中读取一个数据字节,int表示读取到的字节 read(byte[]):int //从目标文件读取字节数组个长度的数据字节 /* 关于字符占用的字节: 如果不涉及任何编码字符集,此时一个字符(中文/英文、数字) 占用2个字节 如果文件的字符集为utf-8,一个英文、数字占用1个字节,一个中文占用3个字节 如果文件的字符集GBK,一个英文、数字占用1个字节,一个中文占用2个字节 */
public static void main(String[] args) throws IOException{ FileInputStream fis = new FileInputStream("a.txt"); // int b = -1; //while((b = fis.read()) != -1){ //单个字节读 byte[] bys = new byte[1024]; int len = -1; while((len = fis.read(bys)) != -1){ //数组个字节读 //System.out.println((char)b); System.out.println(new String(bys,0,len)); } fis.close(); }
FileOutputStream
构造方法:
FileOutputStream(File file){} //创建一个向指定File对象表示的文件中写入数据的文件输出流。 FileOutputStream(String name){} //创建一个向具有指定名称name的文件中写入数据的文件输出流。 FileOutputStream((File file/String name,boolean append){} //若第二个参数值为true,则下次写为追加写入,为false和只写一个参数用法相同
写数据:
write(int): //一次写一个字节,无返回值,失败则抛异常 write(byte[]): //一次写一个字节数组 write(byte[],offset,len): //一次将字节数组中实际有的内容写入文件
public static void main(String[] args) throws IOException{ FileInputStream fis = new FileInputStream("ArrayTest.java"); FileOutputStream fos = new FileOutputStream("copy.java"); //按照单个字节进行复制 /*int by = -1; while((by=fis.read())!=-1){ fos.write(by); } */ //按照字节数组复制 byte[] bys = new byte[20]; int len = -1; //实际读取长度 while((len = fis.read(bys)) != -1){ //数组个字节读 fos.write(bys,0,len)); } fos.close(); fis.close(); }
字节缓冲流:(在文件流的基础上多了缓冲区,提高读写效率)
BufferedInputStream
构造方法:
BufferedInputStream(InputStream){} //创建一个BufferedInputStream并保存其参数,即输出流in,以便将来使用
读数据:
read():int //从此输入流中读取一个数据字节,int表示读取到的字节 read(byte[]):int //从目标文件读取字节数组个长度的数据字节
BufferedOutputStream
构造方法:
BufferedOutputStream(InputStream){}
写数据:
write(int): //一次写一个字节,无返回值,失败则抛异常 write(byte[]): //一次写一个字节数组 write(byte[],offset,len): //一次将字节数组中实际有的内容写入文件
字符流:操作单位是字符,会避免乱码的产生,且读写效率比字节流高,但字符流使用前提是操作的必须是文本文件
抽象基类:(都是抽象类,不能实例化,只能创建子类对象)
Reader
Writer
转换流:
InputStreamReader
构造方法:
InputStreamReader(InputStream in){} //创建一个使用默认字符集的InputStreamReader InputStreamReader(InputStream in,String charsetName){} //创建一个使用指定字符集的InputStreamReader /* 编码: 字符 --> 字节 解码: 字节 --> 字符 */
读数据:
read():int //一次读取一个字符 read(char[]):int //一次读取一个字符数组个字符,返回值表示实际读取的长度
public static void main(String[] args) throws IOException{ InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt")); //按照单个字符读取a.txt中字符并输出 /*int ch = -1; while((ch=isr.read())!=-1){ System.out.println((char) ch); } */ //按照字符数组读取数据 char[] chs = new char[3]; int len = -1; //实际读取长度 while((len = isr.read(chs)) != -1){ //数组个字节读 System.out.println(Arrays.toString(chs)); System.out.println(new String(chs,0,len)); } isr.close(); }
OutputStreamWriter
构造方法:
OutputStreamWriter(OutputStream out){} //创建一个使用默认字符集的OutputStreamWriter OutputStreamWriter(OutputStream out,String charsetName){} //创建一个使用指定字符集的OutputStreamWriter
写数据:
write(int): //一次写一个字符 write(char[],off,len): //写入字符数组的某一部分 高频 write(String,off,len): //写入字符串的某一部分
public static void main(String[] args) throws IOException{ InputStreamReader isr = new InputStreamReader(new FileInputStream("ArrayTest.java")); OutputStreamWriter osr = new OutputStreamWriter(new FileOutputStream("wcopy.java")); //按照单个字符复制 /*int ch = -1; while((ch=isr.read())!=-1){ osw.write(ch); } */ //按照字符数组复制 char[] chs = new char[3]; int len = -1; //实际读取长度 while((len = isr.read(chs)) != -1){ //数组个字节读 osw.write(chs,0,len); } isr.close(); osw.close(); }
字符缓冲流:(按行读写数据)
BufferedReader
构造方法:
BufferedReader(Reader in){} //创建一个使用默认大小输入缓冲区的缓冲字符输入流 BufferedReader(Reader in,int size){} //创建一个使用指定大小输入缓冲区的缓冲字符输入流
读数据:
readLine():String //读取到回车换行就结束,但不会读取回车换行,若读到末尾则返回null
public static void main(String[] args) throws IOException{ //使用字符缓冲流复制文件 BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("Demo01.java"))); PrintWriter pw = new PrintWriter(new FileOutputStream("brcopy.java"),true); //复制 String line = null; while((line = br.readLine())!= null){ pw.println(line); } pw.close(); br.close(); }
BufferedWriter
、PrintWriter
具有自动行刷新功能,默认关闭
构造方法:
PrintWriter(File file/String name){} //创建指定文件创建不具有自动刷新的新的PrintWriter PrintWriter(File file String csn){} //创建具有指定文件和字符集且不具有自动刷新的新的PrintWriter PrintWriter(OutputStream out,boolean autoFlush){} //通过现有的OutputStream创建新的PrintWriter,且带有自动刷新 PrintWriter(Writer out,boolean autoFlush){} //通过现有的Writer创建新的PrintWriter,且带有自动刷新
写数据:
println(String): //自动行刷新 print(String): //自动行刷新不启动
字节流和字符流的选择:
- 字节流:
- 操作的文件是非文本文件
- 需求中分析得出必须按照字节进行读写,此时选择字节流
- 字符流:
- 操作的文件是文本文件
对象流:对对象数据进行读写,对象读写必须序列化
/* 序列化接口: Serializable 叫做标志接口 序列化: 对象数据 --> 字节数据 反序列化: 字节数据 --> 对象数据 反序列化过程: 将文件中读取到的序列版本号与.class文件中的序列版本号进行对比,若相等,则反序列化成功,若不相等,则反序列化失败 序列化版本号: 若实体类中没有序列版本号,则编译时Java为其随机生成版本号,每次编译均重新生成,若实体类中有序列版本号,则后期文件不论如何编译,版本号均不发生改变 idea中设置序列版本号提示步骤: File-->Settings-->Editor-->Inspections-->java-->serialization issues-->勾选 serializab1e without serialversionUID */
ObjectInputSteam
构造方法:
ObjectInputSteam(InputSteam) //创建从InputSteam读取的ObjectInputSteam
读对象数据
readObject():Object
ObjectOutputSteam
构造方法:
ObjectOutputSteam(OutputSteam) //创建写入指定的OutputSteam的ObjectOutputSteam
写对象数据
writeObject(Object)
2.本地有一个存放学生的a.txt文件,内容如下
学号 姓名 性别 年龄 联系电话
11 , 张三, 男 , 23 , 18523214679
12 , 杨丽, 女 , 23 , 15678776678
13 , 小明, 男 , 30 , 13345433451
14 , 小月, 男 , 19 , 18789775567
- 读取
txt
文件的所有内容并显示- 输出所有性别为男的数据
- 对读取到的数据按年龄从大到小输出(忽略第一行)
- 将第三步排序后的数据输出到文件
result.txt
文件中
public class Demo01{
public static void main(String[] args) throws IOException{
//1.读取txt文件的所有内容并显示
//2.输出所有性别为男的数据
//3.对读取到的数据按年龄从大到小输出(忽略第一行)
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("a.txt")));
//3中排序使用 TreeMap
Map<Integer,String> map = new TreeMap<>();
br.readLine(); //忽略第一行
String line = null;
while((line = br.readLine()) != null){
System.out.println(line);
//输出性别为男的数据
String gender = line.split(",")[2].trim();
if("男".equals(gender)){ //equals比较将固定值放在前面
System.out.println(line);
}
//将数据按照年龄降序输出
String ageStr = line.split(",")[3].trim();
int age = Integer.parseInt(ageStr);
map.put(age,line);
}
//将map转换为list,根据value年龄进行降序排列(自定义排序)
List<Map.Entry<String,Integer>> list = new ArrayList<>(map.entrySet());
list.sort((o1,o2)->o2.getValue().compareTo(o1.getValue));
//4.将第三步排序后的数据输出到文件result.txt文件中
PrintWriter pw = new PrintWriter(new FileOutputStream("result.txt"),true);
pw.println(title);
list.forEach(entry->{
String stu = entry.getKey();
System.out.println(stu);
pw.println(stu);
});
pw.close();
br.close();
}
}
3.请写出对map集合的遍历实现(尽可能写出多种方式)
-
for循环
Map<String,Integer> map = new HashMap<>(); for(Map.Entry<String,Integer> entry:map.entrySet()){ System.out.println(entry.getKey() + "-" + entry.getValue()); }
-
lambda表达式
Map<String,Integer> map = new HashMap<>(); map.forEach((k,v)-> System.out.println(k + "-" + v));
-
迭代器
Map<String,Integer> map = new HashMap<>(); Iterator<Map.Entry<String,Integer>> it = map.entrySet().iterator(); while(it.hasNext()){ Map.Entry<String,Integer> entry = it.next(); System.out.println(entry.getKey() + "-" + entry.getValue()); }
4.在Java中如何获得当前时间,如何转换为某字符串格式
LocalDateTime.now(); //格式需要转换 获取当前日期时间
LocalDate.now(); //格式需要转换 获取当前日期,没有时间
new Date(); //格式需要转换
//日期转换类: DateTimeFormatter.ofPattern("");
public static void main(String[] args){
//新日期
//Java8中获取当前日期
LocalDateTime time = LocalDateTime.now();
System.out.println(time);,
//将日期类型转换为某种字符串格式
DateTimeFormatter dtf =
DateTimeFormatter.ofPattern( "yyyy-MM-dd HH : mm : ss" );
String dateStr = dtf.format(time);
System.out.println(dateStr);
//将String类型转换成日期 字符串必须满足Java所识别的日期
String str = "2023-12-12 12:12:12";
LocalDateTime time = LocalDateTime.parse(str,dtf);
System.out.println(time);
//旧日期 Date --> String
Date date = new Date();
SimpleDateFormat sdf =
new SimpleDateFormat("yyyy/MM/dd HH : mm : ss");
String dateStr2 = sdf.format(date);
System.out.println(dateStr2);
//旧日期 String --> Date
String str = "2023/11/11 11:11:11";
try{
Date cur = sdf.parse(str);
System.out.println(cur);
}catch(ParseException e){
e.printStackTrace();
}
}
5.对数组进行去重
int[] ary = {1,2,1,1,1,2,3,4,623,62,2,4,1,0,0};
List<Integer> list = new ArrayList<>();
for(int i:ary){
if(!list,contains(i)){
list.add(i);
}
}
/*
int[] dest = new int[list.size()];
for(int i=0;i<list.size();i++){
dest[i] = list.get(i);
}
*/
Integer[] dest = list.toArray(new Integer[list.size()]);
System.out.println(Arrays.toString(dest));
6.对集合进行去重
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(1);
list.add(4);
list.add(5);
list.add(7);
list.add(1);
list.add(3);
list.add(4);
list.add(5);
List<Integer> list1 = new ArrayList<>();
/*
//以下遍历会产生并发异常 java.util.ConcurrentModificationException
for(Integer i : list){
if(!list1.contains(i)){
list1.add(i);
}else{
list.remove(i);
}
}
*/
//使用迭代器
Iterator<Integer> it = list.iterator();
while(it.hasNext()){
//取出元素
Integer i = it.next();
if(list1.contains(i)){
it.remove();
}else{
list1.add(i);
}
}
System.out.println("去重后的结果为:");
list.forEach(i->System.out.print(i + " "));
7.请写出懒汉式和饿汉式单例模式
//懒汉式
public class SingleTon{
private SingleTon(){}
private static SingleTon singleTon;
public static synchronized SingleTon getInstance(){
if(singleTon == null){
singleTon = new SingleTon();
}
return singleTon;
}
}
//饿汉式
public class SingleTon{
private SingleTon(){}
private static SingleTon singleTon = new SingleTon();
public static SingleTon getInstance(){
return singleTon;
}
}
//饿汉式为什么不加 synchronized,饿汉式是如何保证线程安全的
//对象在类加载时期已经被创建完成,运行期不再创建,多个线程并发访问的是同一个SingleTon
8.写一段代码删除list中所有结尾数字可被2整除的节点
List<String> list = new ArrayList<>();
list.add("aaabbbccc1");
list.add("aaabbbccc2");
list.add("aaabbbccc3");
list.add("aaabbbccc4");
list.add("aaabbbccc5");
list.add("aaabbbccc6");
list.add("aaabbbccc7");
list.add("aaabbbccc8");
//在遍历过程中删除元素,使用迭代器删除
Iterator<Integer> it = list.iterator();
while(it.hasNext()){
String ele = it.next();
//截取数组
int num = Integer.parseInt(ele.substring(9));
if(num%2==0){
it.remove();
}
}
list.forEach(str->System.out.println(str));
9.如何对List根据age进行排序
Collections.sort(list, new Comparator<E>(){
int compare(E o1,E o2){
return o1.getAge().compareTo(o2.getAge());
}
});
list.sort(new Comparator<E>(){
int compare(E o1,E o2){
return o1.getAge().compareTo(o2.getAge());
}
});
简答题
1.列举几个常用的集合类并指出特点
- List集合:有序,有下标,元素可重复
ArrayList
:底层是数组实现的,所以查询元素效率很高,增删元素效率相对LinkedList
而言较低LinkedList
:底层是双向链表实现的,增删元素效率较ArrayList
较高,查询元素效率较低
- Set集合:不可重复集
LinkedHashSet
:底层是散列表 + 链表,用链表来维护元素的顺序,元素是有序的HashSet
:底层是散列表,查询效率最高,元素是无序的TreeSet
:底层是红黑树,对元素进行了排序
- Map集合:
HashMap
:底层是散列表,查询快,元素无序TreeMap
:底层是红黑树,实现了排序LinkedHashMap
:底层是散列表 + 链表,有序
2.Set里的元素是不能重复的,那么用什么方法来区分重复与否呢,是==还是equals(),有何区别?
- 使用
equals()
- 区别:
- == 两侧若为引用类型,比较地址是否相等
equals
方法是Object
提供的方法,在Object
类中,其作用等同于==;但是Java强烈建议在子类中重写该方法,使其根据有逻辑意义。重写后通常都是根据对象的内容值来比较对象是否相等
3.请描述线程的生命状态,并描述sleep(),join(),yield()的区别以及interrupt()的作用
- 新建 --> 就绪 --> 运行 --> 阻塞 --> 死亡
interrupt()
:打断线程的阻塞状态,唤醒线程- 若线程处于阻塞状态,调用该方法,线程被唤醒
- 若线程处于运行状态,调用该方法,无效
yield()
:为其他线程让步,但是调用之后,线程进入就绪状态,而不是阻塞状态join()
:用于实现线程间的同步通讯,调用进入阻塞状态- wait():调用该方法,线程阻塞,唤醒需要其他线程调用
notify()
或notifyAll()
,调用进入阻塞状态
4.简述包装类中new方法和valueof方法的区别
new
:包装类一定创建了一个新的对象valueof()
:完成的是基本数据类型到包装类型的转换,若给出的数值处于常量池的缓存范围之内,此时直接复用常量池中的对象,不再重新创建对象。若数值不处于常量池的范围内,则新创建包装类对象
5.内存溢出和内存泄漏
- 内存泄漏:分配出去的内存回收不回来,无法重新利用,这种现象叫做内存泄漏
- 内存溢出:内存剩余空间不足以分配给请求的资源
- 关系:内存泄漏累积到一定程度会造成内存溢出,但内存溢出不一定是由内存泄漏引起的,也可能是创建的对象太大引起的
- 常见内存溢出:
OutofMemoryError
(OOM):heap溢出StackOverFlowError
(SOF):stack溢出 递归
6.final、finally、finalize的区别是什么
final
:是修饰符,可以用来修饰类(类不能被继承)、方法(方法不能被重写)、变量(一旦初始化不可再改变)finally
:是try...catch
配合使用的,特点是不论之前是否有异常出现,都一定会执行finally
块中的代码,经常用于保存释放资源的代码finalize
:是Object类提供的方法,在GC回收对象前会调用该方法进行最后的资源释放
7.Collection和Collections的区别
Collection
:是接口,其常用子接口有List
、Set
Collections
:是集合操作类,提供了一系列方法用于操作集合