java面试基础题整理(一)

java面试基础题整理

文章目录

基础语法与算法

a++ 与 ++a的区别?

a++: 后置自增,先计算表达式的值,变量值后+1
++a: 前置自增,变量值先+1,再计算表达式的值

&和&&的区别

&有两个用法:

  • 第一个是作为逻辑运算符使用,被当成逻辑运算符时有一个非短路的特性,即当第一个操作数为false,那么第二个操作数仍然执行。
  • 第二个是作为位运算符使用。

&&只有一个用法:

  • 只能作为逻辑运算符使用,和&的区别在于&&有一个短路的特点,即当第一个操作数为false时(因为这个时候已经可以确定表达式的最终结果已经是false),那么第二个操作数不会执行。

***说说jvm

jvm: java虚拟机,
用法1:可以将字节码解释成不同平台都能识别的指令
用法2:在jvm中的垃圾回收机制,可以自动回收系统中的无用内存

***GC是什么? 为什么要有GC?

GC为垃圾回收器,系统中开辟了大量空间,当这些空间无用时,可通过GC进行释放;提高系统执行性能

数组有没有length()这个方法?String有没有length()这个方法?

数组没有length()方法,只有length属性;
String有length()方法;

面向对象

什么是多态?

概念:多态是面向对象的基本特征之一。在java中一个对象的类型可以分为两种,即编译时类型与运行时类型。当一个对象的编译时类型和运行时类型不相同的时候,就发生了所谓的多态。

理解:换句话说,在Java中用一个父类(父接口)的变量指向之类的对象时,就发生了多态。比如 A a = new B(), 其中A是B的父类或者父接口。为什么会有这样的特性?为什么不是子类的变量指向父类的对象?我们可以换一个生活中的例子来理解。比如现在想要一个动物,先声明一个动物的变量:
Animal a;
但是在实际生活中,根本没有一个具体的物种叫动物,所以其实我们需要创建的一个具体的动物的对象,比如狗(动物是狗的父类):
new Dog();
最后将一条狗给与正需要一个动物的‘你’是一个非常合理的行为:
Animal a = new Dog();
反过来却不成立,同样也不合理,你没办法把随便一个动物给一个正需要狗的人。

作用:多态在面向对象编程中有着非常重要的作用。很多架构搭建,设计模式都使用到了多态的特性。多态的最大作用在于,使用一个父类的变量调用方法时,本质上调用的是子类的重写方法。换而言之,让一个Animal a执行 '叫喊’的行为,本质上我们听到的应该是“旺、旺、旺”,因为这个动物其实是Dog。这个特点让多态广泛的运用在架构与程序设计之中,极大的增强了程序的拓展性与维护性。

Integer封装类可以被继承吗?

不能,因为它是final修饰的类。
sun在设计Integer类时添加了final关键字,意为不让其他人随意的拓展和覆盖Integer本来的行为方法。这么设计的作用在于Integer是int类型的包装类,是对基本类型在面向对象的一个补充,很多基础类和第三方的工具类大量引用了这个类型的变量。如果随意覆盖本来的方法会导致这些基础类无法达到预想的结果,从而破坏了底层的实现效果(比如破坏了自动装箱和自动拆箱)。这是sun公司不愿意看到的。如果要对Integer有任何拓展,完全可以通过自定义一个类对Integer进行封装来实现,无需继承。

String可以被继承吗?

不能,同上

int 和 Integer有什么区别?

int为基本类型,Integer为引用类型。
Integer是int类型的包装类,对于int类型在面向对象中的补充。在很多时候没办法直接使用int基本类型,这个时候就需要用到Integer了,比如泛型时,是不能填写基本类型的,必须使用包装类。另外在很多实体类的设计时,通常也是使用包装类,因为包装类可以直接复制为null,而基本类型必须根据不同的基本类型赋予不同的值,操作起来会更加麻烦。

extends和implements区别

implements: 用于实现一个接口
extends: 用于继承一个类
注意:当一个接口需要继承另一个接口时,需要用到extends关键字,而且接口继承多个接口。

构造器Constructor是否可被override?

构造器Constructor不能被继承,因此不能重写,但可以被重载。

接口是否可以继承接口?抽象类是否可实现(implements)接口?抽象类是否可继承具体类(concrete class)?抽象类中是否可以有静态的main方法?

  1. 接口可以继承接口,而且接口继承多个接口
  2. 抽象类可以实现接口
  3. 抽象类可以继承具体类
  4. 抽象类中可以有静态main方法

类的加载机制

  • 什么是类加载机制?
    在一个java程序运行时,虚拟机并不会立刻将这个程序所有的类信息加载到内存中,而是在这个类第一次被使用时进行加载。将一个类加载到虚拟机内存中的过程称为类加载机制。
    注意:一个类在一条JVM进程中只会被加载一次。
  • 类加载的步骤
    类加载可以大致分成3个步骤,分别是:
    • 加载阶段
      在加载阶段需要用到类加载器(ClassLoader类),虚拟机需要完成以下3件事情:

      • 通过一个类的全限定名来获取定义此类的二进制字节流(并没有指明一定要从一个Class文件中获取,可以从其他渠道,譬如:网络、动态生成、数据库等,不同的渠道可以使用不同的类加载器);
      • 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构;
      • 在堆内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口;
    • 连接阶段
      连接阶段和加载阶段是交叉进行的,可能加载阶段还没有完成,连接阶段已经开始,但是加载和连接的开始顺序是固定不变的。连接阶段又可以拆分成3个阶段:

      • 验证:验证是连接阶段的第一步,这一阶段的目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
      • 准备:准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些变量所使用的内存都将在方法区中进行分配。这时候进行内存分配的仅包括类变量(被static修饰的变量),而不包括实例变量,实例变量将会在对象实例化时随着对象一起分配在堆中。其次,这里所说的初始值“通常情况”下是数据类型的零值
      • 解析:
        解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符7类符号引用进行。
    • 初始化阶段
      在类的初始化阶段,JVM负责对类进行初始化,主要是对静态Field初始化。
      对静态Field初始化分两种:

      • 声明静态Field时指定初始值;
      • 使用静态初始化块为静态Field指定初始值;

      初始化顺序会按照源码的编写顺序执行。

  • 类的加载的时机
    当碰到以下几种情况时会触发一个类被加载进虚拟机内存中:
    • 创建类的实例。(new关键字、反射、反序列化等等)
    • 访问类中的某个静态成员
    • 使用反射方式强制创建某个类对应的Class对象。比如:Class.forName(“包名.类名”);
    • 加载某个类的子类
    • 直接使用java.exe命令运行某个主类,当运行某个主类时,程序会先加载该主类

(类加载机制)据下方代码,写出打印结果:

public class SSClass
{
    static
    {
        System.out.println("SSClass");
    }
}    
public class SuperClass extends SSClass
{
    static
    {
        System.out.println("SuperClass init!");
    }
 
    public static int value = 123;
 
    public SuperClass()
    {
        System.out.println("init SuperClass");
    }
}
public class SubClass extends SuperClass
{
    static
    {
        System.out.println("SubClass init");
    }
 
    static int a;
 
    public SubClass()
    {
        System.out.println("init SubClass");
    }
}
public class NotInitialization
{
    public static void main(String[] args)
    {
        System.out.println(SubClass.value);
    }
}

结果:
SSClass
SuperClass init!
123

原因:
对于静态字段,只有直接定义这个字段的类才会被初始化,因此通过其子类来引用父类中定义的静态字段,只会触发父类的初始化而不会触发子类的初始化。

(类加载机制)查看如下代码,写出打印结果:

package jvm.classload;
 
public class StaticTest
{
    public static void main(String[] args)
    {
        staticFunction();
    }
 
    static StaticTest st = new StaticTest();
 
    static
    {
        System.out.println("1");
    }
 
    {
        System.out.println("2");
    }
 
    StaticTest()
    {
        System.out.println("3");
        System.out.println("a="+a+",b="+b);
    }
 
    public static void staticFunction(){
        System.out.println("4");
    }
 
    int a=110;
    static int b =112;
}

结果:
2
3
a=110,b=0
1
4

请简述面向对象的一些基本概念

  1. 封装:将类的属性私有化,使用方法进行封装来获取设置和获取属性值
  2. 继承:子类继承父类,可使用父类的属性和方法; 另一大特性:重写
  3. 多态:父类对象指向子类对象,可以调用子类重写方法
  4. 抽象:如果一个类有相关方法不知具体实现,该方法可设置为抽象方法,所在类就是抽象类;
    具体实现交给子类完成;在应用中抽象类可作为模板使用

java中的常用类,不少于6个?

String,StringBuffer,Date,SimpleDateFormat,Random,Math,System,Runtime等

集合

List, Set, Map是否继承自Collection接口?

List,Set是,Map不是

HashMap的数据结构 存放数据的机制和扩容问题

HashMap底层采用的是哈希表这种数据结构。
哈希表的特点在于查询速度快,时间复杂度为O(1)。

  • 存放数据的机制:
    哈希表实际上是一个数组,当一个键值对(key-value)要放入HashMap中时,首先会获得这个键(key)的hashcode,通过hashcode结合一个哈希函数计算得到一个int类型的值。这个值会充当数组的下标,将该键值对放入数组指定的下标位置。如果该位置已经存在一个键值对数据,则会进行键的对比,如果两个键通过equals方法比较返回true,则hashmap会认为key重复,则直接做一个value覆盖的操作。如果不重复,则会在数组这个位置形成一个链表将两个元素链接起来。这是采用的链地址法解决冲突的一种手段。
  • 扩容问题:
    哈希表有一个填充因子,填充因子是元素个数与数组长度的占比。当元素个数与数组长度的比例达到填充因子的大小时,则需要进行扩容。扩容就是创建一个更大的数组,将老的数组的元素通过哈希函数的计算重新放入新的数组中,从而降低元素越来越多所带来的哈希表查询性能严重下降的问题。但是扩容本身就是一个非常耗时的操作,所以在预先知道数据量很大的情况下,在创建HashMap的时候应该提前设置好底层哈希表的总长度和填充因子的大小,尽可能避免在元素添加过程中频繁的触发扩容机制。

谈谈HashSet的存储原理

HashSet的底层就是由HashMap实现的,所以具体存储原理可以参照HashMap的底层实现。

list和map的应用场景

list: 存储有序且允许重复的单个对象
map: 存储无序且唯一的键值对

常用集合的的底层实现?

  • ArrayList和Vector:
    ArrayList和Vector是List的两个典型实现类。它们底层的实现方案相同。都是采用动态数组的方式实现的。特点是元素有序并且可以重复。ArrayList本身是线程不安全的,Vector是线程安全的。但是Vector比较古老,所以绝大多数情况下应该使用ArrayList。
  • LinkedList:
    LinkedList也是List的一个具有代表性的实现类。底层采用的是双向链表。从中间插入和删除元素的效率更高。
  • HashMap:
    HashMap的是Map集合的常用实现类,底层采用的是哈希表的数据结构存放数据的。特点是插入和查询速度很快。但是随着元素个数越来越多,效率会有所下降,但是仍然是效率很高的一种数据结构。HashMap的key值是无序且不重复的。
  • LinkedHashMap:
    LinkedHashMap底层采用的是哈希表 + 链表的形式存放数据的。因为需要额外的维护一个链表,所谓元素的操作速度会比HashMap略慢。但是因为有链表的存在,遍历元素的速度反而会比HashMap快,并且可以按照元素的插入顺序遍历出来。
  • TreeMap:
    底层采用的是红-黑树实现的。相对于哈希表,红黑树的插入和查询速度要慢,但是红-黑树可以维护元素本身的字典顺序,所以当需要对key进行大小排序的时候,才应该使用TreeMap,否则都应该选择HashMap
  • HashSet/LinkedHashSet/TreeSet:
    Set集合的底层其实就是Map集合实现的。Set集合中包含一个Map集合,每个元素插入到Set集合中时,其实就是插入到底层的Map集合的key的位置,value是一个固定的Object对象,没有任何意义。因此可以发现Set和Map的实现类几乎是一一对应的。

Map怎么实现有序

通过LinkedHashMap实现有序,具体实现同上

Set和Map的底层

Set底层实现是由Map完成的

hashmap和hashtable的区别?

Hashtable:
线程安全,效率会低于HashMap。而且key和value都不能存放null值。
HashMap:
线程不安全,因此效率会高于Hashtable。key和value都可以为null,但是key只能有一个null。

Collections vs Collection

Collections: 集合的工具类
Collection: 用于存储单个对象的集合接口

List vs Set

List: 存储有序且允许重复的对象,可以使用下标遍历
Set: 存储无序且唯一的对象,不能使用下标遍历

集合的工具类有哪些?如何实现集合排序?不使用工具类的话怎么排序?

集合的工具类: Arrays,Collections
工具类排序: Arrays.sort(),Collections.sort()
如果不使用工具类,对于Map和Set可以分别采用TreeMap和TreeSet实现元素的排序。对于List,则可以手写一个排序算法实现元素的排序,比如快速排序、希尔排序等

ArrayList vs LinkedList

ArrayList: 通过数组扩容的方式存储对象,查找和向后追加元素效率高
LinkedList:通过双向链表的方式存储对象,连续位置添加和删除效率高

Set vs Map

联系: Set的存储是通过Map来实现的
区别: Set存储单个对象无序且唯一, Map存储键值对(key-value),key无序且唯一

ConcurrentHashMap vs HashMap vs LinkedHashMap

  • HashMap:
    查询和插入速度极快,但是线程不安全,在多线程情况下在扩容的情况下可能会形成闭环链路,耗光cpu资源。
  • LinkedHashMap:
    基本和HashMap实现类似,多了一个链表来维护元素插入的顺序,因此维护的效率会比HashMap略低。但是因为有链表的存在,遍历效率会高于HashMap。
  • ConcurrentHashMap:
    线程安全,而且采用分段锁的方式进行数据同步,因此相对于Hashtable来说,效率要高。但是因为引入了段的概念,所以每次元素插入或者获取,需要进行两次哈希算法,第一次确定到该元素位于哪一段,第二次才能真正确定到元素位置。因此效率会低于HashMap。不过在多线程情况下,这种性能的牺牲换取数据安全是非常值得的。因此在多线程的情况下应该首选ConcurrentHashMap。

IO流与异常

运行时的异常和一般异常有什么异同?

运行时异常: 可以编译通过,运行时报错
一般异常: 编译时报警告,需提供处理异常方式

try-catch中return和finally哪个优先执行

finally会优先执行,在一段代码中,finally是必须执行的

请说出常见的运行异常

空指针,数组溢出,类型转换,算术异常

有用过NIO吗?NIO的优点在哪里?

NIO底层采用的是内存映射实现的,因此效率会比普通的IO效率要高。

NIO是什么他跟IO有什么区别?

  • NIO是NO Block IO流,是一种基于通道与缓冲区操作的流,是非阻塞的流,读写效率高
  • IO是Block IO流,是一种基于读写操作的流,是阻塞流,读写效率比NIO低

多线程,网络编程

线程池有没有用过,怎么用的,在哪里用的?

在需要创建和销毁大量临时线程时,可以使用线程池

SimpleDataFormat是线程安全的吗?

不是

启动一个线程用的是start()还是run()?

启动线程应该采用start()方法。如果直接调用run()方法,则会像调用普通方法一样执行run方法中的内容。就不会有争抢cpu时间片的过程,达不到线程的效果。

sleep()和wait()有什么区别?

  • sleep()方法:
    sleep()方法是Thread类中的静态方法,当一个线程调用sleep()方法以后,不会释放同步资源锁,其他线程仍然会等待资源锁的释放。
  • wait()方法:
    wait()方法是Object类提供的一个普通方法,而且必须同同步资源锁对象在同步代码块或者同步方法中调用。当调用wait()方法后,当前线程会立刻释放掉同步锁资源。其他线程就有机会获得同步资源锁从而继续往下执行。

谈谈创建多线程的方式

  • 继承Thread的方式
    使用这种方式,当前类就代表一个线程类。可以直接调用start方法启动线程。在线程执行体中要获取当前正在执行的线程只需要通过this关键字就可以了。缺点在于Java的单继承特性,一旦当前类继承了Thread类就没有办法再继承其他类了。
  • 实现Runnable接口的方式
    使用这种方式,当前类并不代表线程。只是代表线程的一个执行体。想要启动线程还必须new一个Thread对象,将执行体对象传过去执行。好处在于Runnable是一个接口,并不耽误继承其他类。缺点在于稍显麻烦,获取当前正在执行的线程不能用this关键字,应该采用 Thread.currentThread();方式。
  • 使用Callable和Future创建线程
    这种方式创建的线程好处在于,可以有返回值

谈谈你对线程安全的理解?如何解决线程的安全问题?

  • 当多个线程互抢资源时,会造成数据混乱;通过线程安全可以确保数据完整性;
  • 线程的安全可以通过以下加锁方式解决:
    1. 同步代码块
    2. 同步方法
    3. 互斥对象锁

Synchronized加在普通方法和静态方法的区别

  • Synchronized修饰普通方法:要确保调用该方法的对象为同一对象,才能确保线程安全
  • Synchronized修饰静态方法:静态方法属于类,可以确保线程安全

同步代码块和同步方法的区别

  • 同步代码块锁的范围是代码块区域
  • 同步方法锁的范围是当前的方法块区域

谈谈线程的生命周期

  1. 实例化线程对象
  2. 启动线程
  3. 进入就绪状态
  4. cpu调度后进入运行状态
  5. 当cpu调度另一线程或当前线程进入阻塞后重新回到3,就绪状态
  6. 线程的结束

谈谈对线程池的理解?jdk提供了哪几种线程池?他们有什么区别?

  • 线程池可以提高线程的创建和销毁的开销
  • jdk提供了以下几种线程池:
    1. newSingleThreadExecutor(单线程的线程池)
      只有一个线程在执行,相对于单线程执行任务
    2. newFixedThreadPool(固定大小的线程池)
      固定线程数处理任务;当任务过多,则固定的线程数谁先执行完任务,就执行剩余任务
    3. newScheduledThreadPool(可缓存的线程池)
      如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程

对多线程的了解?在什么地方使用到过多线程?是否有用过线程池?用的什么线程池?

多线程是程序中的多条执行路径,每个线程在程序中互抢资源;在购票系统,通讯聊天中可以用到多线程
newCachedThreadPool:带缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程

UDP和TCP的区别

  • UDP:无连接的传输协议,数据不安全,性能高
  • TCP:建立连接的传输协议,数据安全,性能低

网络的分层

OSI七层模型: 应用程,表示层,会话层,传输层,网络层,数据链路层,物理层
TCP四层模型: 应用层,传输层,网络层,网络接口层

什么是tcp协议

是一种可靠安全的传输协议,传输效率低,需建立连接,经过三次握手;

Http和Https的区别

  • http: 超文本传输协议,是一种明文的传输协议
  • https: 可理解为安全版的超文本传输,进行加密的一种传输协议

Http的报文里有什么

报文里有: 请求行(requestline)、请求头部(header)、空行和请求数据等4个部分组成

反射与解析

解析XML有几种方式

dom解析,sax解析,pull解析

反射你是怎么理解的?

  • 通过方式机制可以获取私有的属性,方法,构造器

  • 在应用上更灵活性,例如在文件注入方面的使用,通过加载类名,可以得到所属对象的信息

  • 平常用什么工具解析json?xml转换成json字符串展示出来是什么样子的?

  • 解析json的工具有: json-lib,Gson,Fast-json

  • xml解析为json: 标签形式转为: {key1:value1,key2:value2}

编程题

请写出下面代码的运行结果

String s =”123”;
String temp=s;
temp=”1234”;
System.out.println(s);
System.out.println(temp);

打印结果: 123
1234

给定一个不重复的自然数数组{3,5,9,7,4,13,15,0,2,20},已知其最大值20,请将其按从小到大的方式顺序输出,要求算法复杂度为1

public static void main(String[] args) {
		//一个不重复的自然数数组
		int[] array = {3,5,9,7,4,13,15,0,2,20};
		//最大值为20
		int max = 20;
		
		//首先创建一个长度为 最大值+1 的新Integer(消除0的默认值)数据
		Integer[] newarray = new Integer[max + 1];
		//遍历老数组,将每个元素以此插入到新元素的指定位置(规则:每个元素本身对应的下标位置)
		for(int i = 0; i < array.length; i++){
			newarray[array[i]] = array[i];
		}
		
		//遍历新数组,以此打印,遇到null则跳过
		for(int i = 0; i < newarray.length; i++){
			if(newarray[i] != null){
				System.out.print(newarray[i] + " ");
			}
		}
	}

对于1-100范围内的整数I(包括100),找出满足I,I+4,I+10(I+10也在100范围)都是素质的整数I,计算这样I的个数cnt和这些整数的和sum

//个数:7;总和:201
public static void main(String[] args) {
		int count = 0;
		int sum = 0;
		for(int i=1;i<=100;i++){
			int j;
			for(j=2;j<i;j++){
				if(i%j==0||(i+4)%j==0||(i+10)%j==0){
					if(i+10>100){
						System.out.println("个数:"+count+";总和:"+sum);
						return;
					}
					break;
				}
			}
			if(i==j&&i>2){
				count++;
				sum += i;
				System.out.println("素数:"+i);
			}
		}
		
	}

用java代码找出A数组中不存在于B数组的项,例如:A[1,2,3],B[4,2]

public static void main(String[] args) {
		int[] a = {1,2,3,9,5,7};
		int[] b = {2,4,9};
		for(int i=0;i<a.length;i++){
			int j;
			for(j=0;j<b.length;j++){
				if(a[i]==b[j]){
					break;
				}
			}
			if(j==b.length){
				System.out.print(a[i]+"\t");
			}
		}
	}

单例具体实现

1.构造方法私有化
2.调用静态方法获取对象,且每次获取的对象都相同

请在如下函数体中补充完成代码,实现根据输入字符串返回该字符串的倒序形式。如输入“ABC”返回“CBA”。

Public String getReverseString(String str){
	char[] a = str.toCharArray();  //得到字符数组
		for(int i=0;i<a.length/2;i++){  
			char t = a[i];  //首尾交换
			a[i] = a[a.length-1-i];
			a[a.length-1-i] = t;
		}
		String s = new String(a);
		return s;
}

下面的程序代码输出的结果是多少?

public class smallT{
   public static void main(String  args[]){
      smallT  t=new smallT();
      Int  b=t.get();
      System.out.println(b);
    }
    public int get(){
        try{
            return  1;
        }
        finally{
           return 2;
        }   
    }
}

2
因为finally优先级比return高,哪怕return了,最终也要执行finally中的代码

写一个函数,2 个参数,1 个字符串,1 个字节数,返回截取的字符串,要求字符串中的中文不能出现乱码:如(“我ABC”,4)应该截为“我AB”,输入(“我ABC 汉DEF”,6)应该输出为“我ABC”而不是“我ABC+汉的半个”。

//方式1:
public static void main(String[] args) {
		subString("我abc",4);
		subString("我abc汉EF",6);
	}
	private static void subString(String str, int len) {
		char[] b = str.toCharArray();
		StringBuffer sb = new StringBuffer();
		for(int i=0;i<len;i++){
			if(b[i]>=0&&b[i]<=255){
				sb.append(b[i]);
			}else{
				if(i!=len-1){
					sb.append(b[i]);
				}
				len--;
			}
		}
		System.out.println(sb.toString());
	}
}
方式2:
utf-8编码,一个汉字占3个字节,gbk编码,一个汉字占2个字节
 截取字符串:在gbk编码中,汉字一半的字节都是小于0的

	public static String splitFirst(String str, int bytesize) throws UnsupportedEncodingException {
		byte[] buf = str.getBytes("GBK");
		int num = 0;
		boolean bChineseFirstHalf = false;
		for (int i = 0; i < bytesize; i++) {
			if (buf[i] < 0 && !bChineseFirstHalf) {
				bChineseFirstHalf = true;
			} else {
				num++;
				bChineseFirstHalf = false;
			}
		}

		return str.substring(0, num);
	}

给定的一个文件“a.txt”,找出所有符合“Japanes”字符串的个数,并修改为“Japanese”

BufferedReader br = new BufferedReader(new FileReader("a.txt"));
		StringBuffer sb = new StringBuffer();
		String msg;
		while((msg=br.readLine())!=null){
			sb.append(msg);
		}
		msg = sb.toString();
		br.close();
		int count = 0;
		int index = msg.indexOf("Japanese");
		while(index>=0){
			count++;
			msg = msg.substring(index+1);
			index = msg.indexOf("Japanese");
		}
		System.out.println("匹配的个数为:"+count);
		BufferedWriter bw = new BufferedWriter(new FileWriter("a.txt"));
		msg = sb.toString();
		msg = msg.replace("Japanese", "Japanese");
		bw.write(msg);
		bw.close();

面试题为手动整理出来的,按照自己的理解进行了思路分析及解答,难免有纰漏之处,望大家指正,希望能给大家带来帮助!


使我有洛阳二顷田,安能佩六国相印? - -苏秦

猜你喜欢

转载自blog.csdn.net/weixin_44511845/article/details/100827612