关于Java的语言问题汇总

本文详细探讨了Java语言的核心特性,包括面向对象的三大特征:封装、继承和多态,以及Java的运行机制。讲解了JVM的工作原理、JRE和JDK的区别,以及垃圾回收(GC)的概念。同时,深入分析了Java中的数据类型、类型转换、字符串连接、内存管理、多态性、内部类和接口的使用,以及JDBC和数据库交互的基本概念。此外,还涵盖了异常处理、HTTP工作原理、JSP的工作模式以及会话管理等关键知识点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第一章

1.面向对象程序设计的基本特征有哪些?

1.封装
指将对象相关的状态信息和行为捆绑为一个逻辑单元,通过封装可以隐藏一个类的实现细节。
2.继承
通过类之间的继承,实现了代码的复用,在子类中可以新增属性和方法,并且可以重写父类方法的具体实现方式。
3.多态
多态在java中是指对象变量是多态的,一个类型为A的变量既可以指向类型为A的对象,又可以指向A的任何子类的对象。一个接口类型的变量也可以指向该接口实现类的对象。

2.java语言的运行机制如何?

计算机高级编程语言按其程序的执行方式可以分为编译型语言解释型语言

编译型语言是指使用专用的编译器,针对特定的操作系统将源程序代码一次性翻译成计算机能识别的机器指令。例如C,C++等都属于编译型语言。

解释型语言是指使用专门的解释器,将源程序代码逐条地解释成特定平台的机器指令,解释一句执行一句,类似于“同声翻译”。例如ASP,PHP等都属于解释型语言。

java既不是编译型语言也不是解释型语言,它是编译型和解释型语言的结合体。首先采用通用的java编译器将java源程序编译成与平台无关的字节码文件(class文件) ,然后由java虚拟机(JVM)对字节码文件解释执行。即:

java源代码–>java bytecode(.class file)–>经过JVM解释执行。

编译型和解释型语言都有各自的优缺点。
编译型语言会在程序第一次执行时将其全部编译成与当前系统平台相对应的机器指令,在后续执行时,直接运行第一次编译的结果,减少了编译次数,提高了程序运行效率,但是程序在第一编译时与系统平台相对应,因此移植性特别差。
解释型语言在程序每次运行时都要将源程序解释成当前系统平台相对应的机器指令,因此每次运行都需要解释并执行,运行效率较低,但是移植性强。

java采取了折中的方案。java首次运行时采取编译机制将源程序编译为java字节码文件,该字节码与系统平台无关,是介于源代码和机器指令之间的一种状态。在后续运行时,采取解释机制将java字节码解释成与系统平台对应的机器指令。这样既减少了编译次数,又增强了程序的可移植性,因此被称为“一次编译,多处运行”。因为.class文件并不是所有机器都能够识别的,所以才加一层虚拟层-虚拟机嘛。

提示:java字节码具有平台无关性,可以在各种不同系统平台中运行,但是,需要有不同版本的java虚拟机,不同系统平台的java运行环境其java虚拟机是不一样的。

其实,现在将编程语言分为“编译型”和“解释型”已经不够严谨了,更多资料参考:
1.Java 是编译型语言还是解释型语言?-知乎
2.R大的干货
3.程序的编译与解释有什么区别?-知乎

3.什么是JVM?有什么用?工作机制如何?

JVM是一个虚构出来的计算机,可在实际的计算机上模拟各种计算机功能。JVM有自己完善的硬件架构,例如处理器,堆栈和寄存器等,还有相应的指令系统。

JVM屏蔽了与具体操作系统平台相关的信息,从而实现了java程序只需生成在JVM上运行的字节码文件,就可以在各种平台上不加修改地运行。JVM是java运行环境的最核心部分,是运行java程序的最基本环境。

4.什么是JRE?

JRE是java runtime environment的简称,即java运行时环境,它是java程序运行所必须的环境集合,主要由java虚拟机,java平台核心类和若干支持文件组成。JRE不包括编译器,调试器以及其他工具。

5.什么是JDK?

JDK是java development kit的简称,即java开发工具包。JDK提供了java的开发环境和运行环境。

JDK中除了包含JRE的全部内容外,还包括开发者用以编译,调试和运行java程序的工具。比如:
javac,jar,javadoc,jdb(debugger,查错工具)等。

6.JDK,JRE,JVM之间有什么区别?

它们是一种包含关系,范围由大到小是JDK,JRE,JVM。

7.什么是GC?GC的工作原理如何?

java语言没有提供释放以分配内存的显示操作方法,资源回收工作全部交由GC来完成,程序员不能精确地控制垃圾回收的时机。java的内存管理实际上就是对象的管理,其中包括对象的分配和释放。

8.JDK安装时设置PATH和CLASSPATH环境变量有何作用?

PATH的设置已经非常熟悉了。CLASSPATH环境变量的作用是,让java执行环境找到指定的java程序对应的class文件以及程序中引用的其他class文件。

JDK在默认情况下会到当前工作目录下以及JDK的lib目录下寻找所需的class文件。

9.如何将程序中的文档注释提取出来生成说明文档?

javaDoc从源代码中抽取类,方法,成员等文档注释形成一个与源代码配套的API帮助文档。javadoc -help试试,在看别人的代码时,可以注释一些,然后生成javadoc,方便查看。

10.怎样制作双击就运行的jar文件?

java文件编译后会生成class文件,一个java类对应一个class文件,为了避免class文件过多,方便用户使用,开发者在为用户发布部署之前需要将class文件压缩。jar工具的主要用途是可以完成一系列相关的程序文件压缩为一个文件。

在windows下可以写一个bat文件,内容为java -jar hello.jar。双击即可运行。

第二章

1.用public,protected,private修饰方法有什么区别?

可以总结出访问控制的限制程度由高到低为:

private->default->protected->public,权限等级差别为:
类内部,同一包中,子类中,其他

2.this关键字有什么含义?在哪些情况下应用?

java中为了解决变量的命名冲突和不确定性问题,引入了关键字this。this代表当前类的一个实例,它经常出现在方法和构造方法中,具体使用情况有以下3种:

1.返回调用当前方法的对象的引用
2.在构造方法中调用当前类中的其他构造方法
3.当方法参数名和成员变量名相同时,用于区分参数名和成员变量名

3.final关键字有什么含义?具体如何应用?

分三种情况:

1.final修饰类

表示该类不能再被其他类继承

2.final修饰成员变量

表示该变量是一个常量,在需要使用常量的场合,可以使用final关键字定义。如果final修饰的是一个简单类型的变量,那么变量值一旦初始化后,将不能修改。如果final修饰的是一个引用类型的变量,那么该变量的引用不可以改变,但可以通过该引用修改引用对象的属性值。

3.final修饰方法

表示该方法不可以在子类中重写(覆盖)

4.instanceof关键字有什么含义?如何应用?

当instanceof左操作数是右操作数指定的类型或者子类类型时返回true,反之则返回false。

instanceof关键字主要作用是判断一个对象是否为指定类型。在变量使用多态时,可以使用instanceof关键字进行变量的类型判断,然后执行不同的操作。

5.java中有哪些数据类型?

java语言数据类型分为两种,一种是简单数据类型,另一种是引用数据类型。简单数据类型都有着固定的长度。如下:
这里写图片描述

注意:char类型为两个字节采用unicode编码,因此,无论是简单的字母还是汉字,在java中都是占用两个字节。

除了8种简单数据类型之外的所有数据类型都被称为引用数据类型,引用数据类型的大小统一为4字节,记录的是其引用对象的地址

java属于强类型语言,在定义变量时必须严格指定变量类型。java类型主要分为简单类型和引用类型,简单类型变量直接存储变量值,而引用类型变量存储的却是地址。

6.如何解决double和float精度不准的问题?

实验下列代码:

System.out.println(0.05+0.01);
System.out.println(1.0-0.42);
System.out.println(4.015*100);
System.out.println(123.3/100);

输出为:

0.060000000000000005
0.5800000000000001
401.49999999999994
1.2329999999999999

并不是想要的结果。

一个原则:float和double只能用来做科学计算或者是工程计算,但在商业计算中我们要用java.math.BigDecimal。通过使用BigDecimal类可以解决上述问题。

通过JDK帮助文档的原文解释,我们指知道浮点数是无法在计算机中国准确表示的,例如0.1在计算机中只是表示成了一个近似值,因此,对浮点数运算时具有不可预知性。如果两个浮点操作数的误差可以相互抵消,计算结果就能保持正确。

7.float f = 3.4语句是否正确?

先上回答,不正确。

java中的类型可分为基本类型和引用类型,不同类型之间的变量赋值时,首先需要先进行类型转换,然后再进行赋值。

java类型转换可分为自动转换和强制转换两种。自动转换过程不需要程序员使用代码进行干预,而强制转换则需要程序员显示指定要转换的类型。

自动转换需要满足一下基本条件:

1.转换双方的类型必须兼容
2.目标类型比源类型范围要大

java中整形变量默认为int类型,如果需要申明long类型的常量,应该在后面加”L”或”l”。

int i = 3;
long l = 3L;

java中浮点型常量默认为double型,如要申明一个float型,则需在数字后面加”F”或”f”。

double d = 3.14;
float f = 3.14f;

8.成员变量和局部变量有什么区别?

1.public,protected,private,static等修饰符可用于修饰成员变量,但不能修饰局部变量。两者都可以使用final修饰。
2.成员变量存储在堆内存中,局部变量存储在栈内存中。
3.作用域不同,成员变量在整个类中有效,局部变量在方法体中有效,在方法体之外不可见。
4.成员变量可以默认初始化,局部变量必须显示初始化。

9.变量之间传值时可分为值传递和引用传递,那么它们有何区别?

存储机制:

简单类型变量是直接在栈内存中开辟存储空间存储变量值。

引用类型变量是由引用空间和存储空间两部分构成,引用空间在栈内存中,存储空间在堆内存中,存储空间负责存储变量值,引用空间负责存放存储空间的首地址。

引用类型变量和简单变量都属于值传递,不同的是简单变量传递的是内容本身,而引用变量传递的是引用地址。

10.x+=y和x=x+y两种方式有区别吗?

虽然x+=y和x=x+y在平时可以通用,还是有差别,差别在于+=除了实现功能之外还要进行一次类型的强制转换。

short ss = 3;
ss+=1;//编译通过
ss=ss+1//编译错误

11.”+”操作符在java内部是如何实现字符串连接的?

java不支持运算符重载,但在java内部提供了一些特殊运算符,他们具有重载的特征。例如”+”在实现字符串连接时,操作数可以是两个字符串,也可以是一个字符串和一个其他类型。示例:

String a = "a";
String b = "b";
String c = a+b;
String d = c+1;

将上述代码编译后再反编译,可以得到:

String a = "a";
String b = "b";
String c = (new StringBuilder(String.valueOf(a)).append(b)).toString();
String d = (new StringBuilder(String.valueOf(c)).append(1)).toString();

String类代表大小固定的字符串,一旦申明定义后,内容和大小将不可改变。String类中提供的所有字符串操作方法,都是操作结果创建了一个新的String对象并返回。

为了考虑效率问题,在底层java采用了StringBuilder类,该类代表大小可变的字符串,利用StringBuilder类的append和insert方法可以在原字符串基础上修改。

12.==和equals()有什么区别?

==是一个关系运算符,用于判断两个简单变量的值是否相等,或两个引用变量的引用地址是否相等。

equals()是一个方法,用于判断引用变量引用地址指向的存储内容是否相同。(该语义功能是通过重写equals方法来获得的)

13.使用String s = new String(“a”)和String s = “a”有什么区别?

java为String类型提供了缓冲池机制,当使用双引号定义对象时,java环境首先去字符串缓冲池中寻找相同内容的字符串,如果存在就直接拿出来使用,如何不存在则创建一个新的字符串放在缓冲池中,一般建议采用String s = "a"的方式,效率高。

14.如何实现数组的复制?

使用System类的一个静态方法arraycopy(),该方法定义如下:

static void arraycopy(Object src,int srcPos,Object dest,int destPos,int length)

第三章

1.什么是类,对象,属性和方法?

类是对一类事物的抽象描述。
对象是类的一个具体实现,代表一个实实在在的个体。
属性是对一类相同特征的抽象。
方法是对一类相同行为的抽象。

2.什么是包?有什么好处?

为了避免类名重复的冲突,java引入了包机制,通过包可以组织程序的类,在不同的包中允许使用相同的名字。例如JDK提供的Date类就存在两个,分别在java.sql和java.util下。

3.什么是抽象类?有什么好处?

有abstract关键字修饰,允许包含未实现方法的类被称为抽象类。
有些情况下,在定义类时,可能有些方法需要具体实现,有些方法无法确定具体的实现方式,这时可以将该类定义成抽象类,无法具体实现的方法定义成抽象方法。

抽象类在使用时,注意几点:
1.抽象类不能实例化,即不能创建对象,只能作为父类用于被继承
2.子类继承一个抽象类后,必须实现父类中的所有抽象方法,否则子类也要定义为抽象类
3.抽象类中可以包含抽象方法,也可以不包含
4.如何包含抽象方法,必须定义为抽象类

4.什么是接口?有什么好处?

接口是方法声明和常量值定义的集合。定义成员变量时,会自动加上static和final关键字声明。

接口在使用时,需要注意以下几点:
1.接口只包含方法声明和常量定义,即使定义普通属性,该属性编译后也将变为常量
2.当其他类实现该接口时,接口中定义的所有方法都要求全部实现,否则需要定义成抽象类
3.一个类可以实现多个接口
4.定义接口时可以使用继承,接口之间允许多继承

5.什么是多态?

在java语言中,多态主要是指对象变量的多态,即一个A类型的变量可以既指向A类型的对象,又可以指向其子类的对象。

6.什么是内部类?有什么好处?

示例:

class A{
    private int age;
    class B{

    }
    public int getAge(){
        return age;
    }
}

在上述代码中,类A和B是并列关系,编译后会产生A.class和B.class两个字节码文件,它们是两个互不相同的类。

内部类主要有以下优点
1.内部类对象能访问其所处类的私有属性和方法。
2.内部类能够隐蔽起来,不被同一包中的其他类访问。如果一个类只对某个类提供使用,那么可以将其定义为内部类。
3.匿名内部类可以方便地用在回调方法中。

内部类的主要特征
1.内部类可以声明为抽象类,因此可以被其他的内部类继承,也可以声明为final的。
2.和外部类不同,内部类可以声明为private或protected,外部类只能用public和default。
3.内部类可以声明为static的,但此时就不能再使用外层封装的非static的成员变量
4.非static的内部类的成员不能声明为static的,只有在顶层类或static的内部类中才可以声明static成员。

7.使用抽象类和接口的场景?

区别:

1.定义格式不同

2.使用方式不同,一个子类只能继承一个抽象类,对于接口,一个类可以实现多个接口

3.设计理念不同,抽象类用于继承,表示”is a “的关系,而接口用于实现,表示”like a”的关系。例如具有报警功能的门,可以继承一个抽象的门类,实现一个报警器的接口

8.关于方法重载

方法的返回类型不是区分方法相同的标识,因此在方法重载时,同一个类中不能有两个方法名相同,参数相同,返回类型不同的方法。

在进行方法重载时,需要遵守以下几个重载规则:
1.方法名相同
2.参数列表必须不同
3.返回值类型可以不同
4.重载方法可以通过this关键字相互调用

9.static修饰的方法能否在子类中重写?

具有static修饰的父类方法是无法在子类中进行重写的,因为static关键字修饰的方法或者属性,表示该属性或方法与类相关。当JVM运行static修饰的方法时,解释器不会检查当前变量指向哪个类型实例,而是直接调用当前变量类型的方法。示例:

A test = new B();//结果为a
//        B test = new B();//结果为b
test.show();

class A{
    public static void show(){
        System.out.println("a");
    }
}
class B extends A{
    public static void show(){
        System.out.println("b");
    }
}

10.java中动态绑定是什么意思?

将一个方法调用同一个方法主体连接到一起称为“绑定”(binding)。如果在程序运行之前执行绑定,由编译器决定方法调用的程序,称为“早期绑定”或“静态绑定”。如果绑定过程在程序运行期间运行,以对象的类型为基础,则称为“后期绑定”或“动态绑定”

如果一种语言实现了后期绑定,同时必须提供一些机制,可以在运行期间判断对象的实际类型,并分别调用适当的方法,即编译器此时依然不知道对象的类型,但方法调用机制能够自己去调查,找到正确的方法主体。java方法的执行主要采用动态绑定技术,在程序运行时,虚拟机将调用对象实际类型所限定的方法。

java方法在调用过程中主要经历了以下过程
1.编译器查看对象变量的声明类型和方法名,通过声明类型找到方法列表
2.编译器查看调用方法时提供的参数类型
3.如果方法由private,static和final修饰或者是构造器,编译器就可以确定调用哪一种方法,即采取静态绑定技术。如果不是上述情况,就使用动态绑定技术,执行后续过程
4.虚拟机提取对象的实际类型的方法表
5.虚拟机搜索方法签名
6.调用方法

11.java中是如何实现多态的?实现机制是什么?

java是借助方法的重写和重载实现多态性的。重写是父类与子类之间多态性的一种表现,重载是一个类中多态性的一种表现。

在JVM中,对象的引用其实是指向一个句柄(handle)的指针,该句柄包含一对指针,一个指针指向一张表格,实际上该表格也有两个指针(一个指针指向包含了对象的方法表,另外一个指向对象类型,表明该对象所属的类型),另一个指针指向一块从java堆中分配的内存空间,该空间用于存储对象数据

java通过将子类对象引用赋值给父类对象的引用变量来实现动态方法调用,这种实现方式遵循的原则如下:

1.如果a是类A的一个引用,那么a可以指向类A的实例,或者是类A的子类实例
2.如果a是接口A的一个引用,那么a必须指向实现了接口A的一个类的实例
3.当父类对象引用变量引用子类对象时,被引用对象的类型决定了调用的方法,而不是引用变量的类型决定,但是这个被调用的方法必须是在父类中定义过的,即被子类重写的方法。

12.创建类的对象时,类中各成员的执行顺序是什么?

顺序如下

1.父类静态成员和静态初始化块,按在代码中出现的顺序依次执行。
2.子类静态成员和静态初始化块,按在代码中出现的顺序依次执行。
3.父类实例成员和实例初始化块,按在代码中出现的顺序依次执行。
4.执行父类构造方法
5.子类实例成员和实例初始化块,按在代码中出现的顺序依次执行。
6.执行子类构造方法

13.如何调用内部类中的方法?

内部类是指在一个外部类的内部再定义一个类,内部类作为外部类的一个成员,并且依附于外部类而存在的。可以在一个外部类中定义多个内部类,这些内部类在逻辑上是一个整体,与外部类之间是从属关系。在内部类中可以访问外部类定义的私有成员。示例代码:

class Outer{
    private int size;
    public class Inner{
        public void doStuff(){
            size++;
            System.out.println(size);
        }
    }
    public void testInner(){
        Inner i = new Inner();
        i.doStuff();
    }
}

在上述代码中,在Outer类中定义了属性size,内部类Inner和方法testInner,在方法中调用内部类,直接通过“对象.方法”形式即可调用。如果在其他类中需要调用Inner类中的方法,可以使用如下方式:

//方法一
Outer.Inner in = new Outer().new Inner();
in.doStuff();
//方法二
Outer o = new Outer();
Outer.Inner in1 = o.new Inner();
in1.doStuff();

内部类可以通过static修饰,称为静态内部类。在静态内部类中调用外部类成员,成员也要求用static修饰。如果内部类用static修饰,可以使用如下格式调用其方法:

Outer.Inner in = new Outer.Inner();
in.doStuff();

14.当内部类和外部类的成员名称相同时,如何在内部类中调用外部类的成员

当出现该情况时,内部类中调用外部类的成员需要显式指明,否则默认为内部类中的成员。通过通过“外部类.this.成员”的形式引用外部类的成员。

15.匿名内部类如何访问外部方法的局部变量或参数?

匿名内部类一般定义在一个方法的内部,如果要访问该方法的参数或者方法中定义的变量,则这些参数和变量必须使用final修饰。

虽然匿名内部类定义在方法的内部,但在编译时内部类与外部类中的方法属于同一个级别,外部类中方法的变量或参数只是方法的局部变量,这些变量或参数的作用域只在当前方法内部有效。但是,如果这些变量使用final修饰,内部类就可以保存方法变量的备份,即使方法销毁也能保证内部类在访问时不会出现访问不到的情况。示例代码如下:

class Test{
    private int m;
    public void go(int x,final int y){
        int a=-1;
        final int b = 0;
        new Thread(){
            @Override
            public void run() {
                //访问外部类的成员变量
                System.out.println("m="+m);
                System.out.println("x="+x);//不合法
                //访问final修饰的方法参数
                System.out.println("y="+y);
                System.out.println("a="+a);//不合法
                //访问final修饰的方法变量
                System.out.println("b="+b);
            }
        }.start();
    }
}

在java8之前这段代码的上述描述是对的。但java8更精简了代码。

java8开始,可以不加final修饰符,由系统默认添加。java将这个功能称为:Effectively final 功能
参考:

1.java为什么匿名内部类的参数引用时final?
2.Java8增加功能-Effectively final 功能
3.Java8中 局部内部类访问的局部变量不必用final修饰

16.java异常处理机制是怎样的?

java程序在运行时遇到的情况大致可以分为两类,即错误(error)和异常(exception)。error是指java虚拟机内部发生错误,有虚拟机产生并抛出,例如资源耗尽等情况,通常java程序不对这些例外进行处理。

exception类有可以分为运行时异常(runtimeexception)和非运行时异常(如ioexception等)。java编译器要求java程序必须捕获或者声明所有的非运行时异常,但对运行时异常可以不做处理,因此,在编码时非运行时异常如果不处理,编译时会出错。

17.常见的runtimeexception异常有哪些?

虽然java编译器对运行时异常不强制要求处理,但实际开发中为了程序的健壮性,还是有必要处理的。

常见runtimeexception异常:

1.ArithmeticException,数学计算异常
2.NullPointerException,空指针异常
3.NegativeArraySizeException,负数组长度异常
4.ArrayOutOfBoundException,数组索引越界异常
5.ClassNotFoundException,类文件未找到异常
6.ClassCastException,类型强制转换异常
7.SecurityException,违背安全原则异常

常见的非runtimeexception异常:

1.NoSuchMethodException,方法未找到异常
2.EOFException,文件已结束异常
3.IOException,输入输出异常
4.FileNotFoundException,文件未找到异常
5.NumberFormatException,字符串转换为数字异常
6.SQLException,操作数据库异常

18.java中异常处理的方式有哪些?

主要有两种方式:

1.try-catch-finally
2.throws

异常方式的选择

try-catch方式允许开发者在程序发生异常时,对异常部分进行补救或处理,例如尝试通过另一种替代方案或将异常信息保存等;而throws方式仅仅是在发生异常后将异常抛给上一级,让调用该方法的方法处理异常。

原则

1.如果方法中前面代码发生异常后,后面代码需要继续执行,选择try-catch
2.如果方法中代码发生异常后,有替代方案或需要特殊处理,选择try-catch
3.如果方法中前面代码发生异常后,无法处理,后面代码无法正常执行,选择throws
4.根据具体情况而定,有些厂商规定异常在某一特定层使用try-catch,其他层用throws等。

19.为什么使用自定义异常?自定义异常如何使用?

自定义的主要原因是,java提供的异常类在某些情况下还是不能满足实际需求。通常是下面几种情况:

1.系统中有些错误符合java语言,但是不符合业务逻辑的,例如年龄大于200,用户不存在等,这些都属于描述业务逻辑错误的。

2.在分层的软件结构中,通常在表现层统一对系统其他层次的异常进行捕获处理,如果表现层需要针对系统各层不同的异常执行不同处理,就需要使用自定义异常。因为java提供的异常类在系统每个层次都有可能产生,使得在异常处理时不能区分处理。

第四章

1.如何获取文件的属性信息?

在java中,提供了对文件及目录进行操作的File类,使用该类的方法可以获取文件相关的信息。

在File类中,包含length()方法,主要功能是返回文件的大小。如何文件不存在或者文件为空时,length()方法返回0

2.如何实现文件的创建,删除和移动?

1.文件创建

使用File类的createNewFile()和createTempFile()方法,可以实现创建文件的功能。
注意:在创建文件时,必须保证父目录正确存在,否则文件创建异常。

2.文件删除

使用File类的delete()方法,可以实现删除文件的功能。如果此路径名表示一个目录,则此目录必须为空才能删除。当且仅当成功删除文件或目录时,返回true,否则返回false。

3.文件移动

使用File类中的renameTo()方法,可以实现文件的重命名和移动功能。renameTo()方法在对文件重命名时可以修改路径和文件名,当且仅当重命名成功时,返回true,否则为false。示例:

//源文件
File file = new File("F:\\b.txt");
//目的文件
File dest = new File("D:\\a.txt");
//将其从F盘移动到D盘并命名为a.txt
file.renameTo(dest);

3.如何创建和删除文件夹?

1.创建文件夹

使用File类中的mkdir()和mkdirs()方法,可以实现创建文件夹的功能。mkdir()方法用于创建当前目录,必须保证父目录存在,否则创建失败;mkdirs()方法可用于创建多层目录,如果父目录不存在,可以连同父目录一起创建。

2.删除文件夹

使用File类中的delete()方法,在删除文件夹时,要求所删除的文件夹为空,否则删除失败。下面代码演示删除文件夹:

public static void deleteFile(File file){
        //判断目录或者文件是否存在
        if(file.exists()){
            //如果是文件
            if(file.isFile()){
                file.delete();
            }else if(file.isDirectory()){
                //获取目录中的子目录和文件
                File[] files = file.listFiles();
                //递归调用deleteFile方法
                for (int i = 0; i < files.length; i++) {
                    deleteFile(files[i]);
                }
            }
            //尝试删除目录
            file.delete();
        }else{
            System.out.println("文件不存在");
        }
    }

4.如何遍历目录中的所有文件?

使用File类中的listFiles()方法可以获取文件夹中的文件和子文件夹信息。示例代码:

public static void fileList(File file){
        if(file.isFile()){
            //如果是文件,直接输出
            System.out.println("文件--->"+file.getName());
        }else if(file.isDirectory()){
            System.out.println("文件夹--->"+file.getName());
            //获取目录中的子目录和文件
            File[] files = file.listFiles();
            //递归调用deleteFile方法
            for (int i = 0; i < files.length; i++) {
                fileList(files[i]);
            }
        }
    }

5.什么是流?如何分类?具体包含哪些类?

1.流的定义

流是一个很形象的概念,大部分的应用都接收某种形式的数据输入,并产生某种形式的数据输出。当程序需要读取数据的时候,就会开启一个通向数据源的流,这个数据源可以是文件,内存或者是网络连接。程序写入过程与读取过程相似。

2.流的分类

按照流向不同,分为输入流和输出流;按照处理单位不同,分为字节流和字符流

2.1字节流

字节流在数据读取和写入时以字节为单位,包含InputStream和OutputStream两个基础类。

关于InputStream,其子类主要有以下几种:

1.ByteArrayInputStream,把内存中的一个缓冲区作为InputStream使用。(ByteArrayOutputStream)

2.StringBufferInputStream,把一个String对象作为InputStream

3.FileInputStream,把一个文件作为InputStream,实现对文件的读取操作(FileOutputStream)

4.PipedInputStream,实现了pipe的概念,主要在线程中使用(PipedOutputStream)

5.SequenceInputStream,把多个InputStream合并为一个InputStream(SequenceOutputStream)

6.DataInputStream,从Stream中读取基本类型(int等)数据。(DataOutputStream)

7.BufferedInputStream,使用缓冲区读取字节数据(BufferedOutputStream)

8.LineNumberInputStream,记录InputStream内的行数,然后可以调用getLineNumber()和setLineNumber(int)方法

2.2字符流

字符流在数据读取和写入时以字符为单位,16位的unicode字符流,字符流包含Reader和Writer两个基础类。

Reader用于读取,其子类主要有以下几种:

1.CharArrayReader,与ByteArrayInputStream对应
2.StringReader,与StringBufferInputStream对应
3.FileReader,与FileInputStream对应
4.PipedReader,与PipedInputStream对应
5.BufferedReader,与BufferedInputStream对应
6.LineNumberReader,与LineNumberInputStream对应
7.PushBackReader,与PushBackInputStream对应

6.如何实现字节流和字符流之间的转换?

一般在处理图片,多媒体等原始字节信息时,选用字节流;如果文件包含的是字符信息,应选用字符流。

字节流和字符流之间是可以相互转换的,但是需要注意一个字符等于两个字节的问题。

InputStreamReader

它是字节流通向字符流的桥梁,可完成字节流到字符流的转化。它使用指定的字符集读取字节并将其解码为字符,使用的字符可以显式给定,否则接收平台默认的字符集。中文操作系统的默认编码为GBK。

每次调用InputStreamReader的read()方法都会导致从基础输入流读取一个或多个字节。要启用从字节到字符的有效转换,可以提前从基础流读取更多的自己,使其超过满足当前读取操作所需的字节。为了达到最高效率,可以考虑使用BufferedReader包装InputStreamReader。代码如下:

public static void test(File file){
        FileInputStream fis = null;//字节流
        InputStreamReader isr = null;//转换
        BufferedReader br = null;//字符流
        //创建字节输出流
        try{
            fis = new FileInputStream("/home/zeng/Desktop/debug.txt");//测试文件,中英文结合,用UTF-16LE进行编码
            //利用InputStreamReader来包装FileInputStream字节流,并指定字节转换为字符的编码为UTF-16LE
            isr = new InputStreamReader(fis,"UTF-16LE");//正确
//            isr = new InputStreamReader(fis,"UTF-8");//乱码
            //利用BufferedReader包装InputStreamReader
            br = new BufferedReader(isr);
            //读取文件中的一行字符
            String s = br.readLine();
            System.out.println(s);
        }catch(Exception e){
            e.printStackTrace();
        }finally {
            if(br!=null){
                try{
                    br.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
            if(isr!=null){
                try{
                    isr.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
            if(fis!=null){
                try{
                    fis.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
        }
    }

对应的,OutputStreamWriter是字符流通向字节流的桥梁,可完成字符流到字节流的转化,同InputStreamReader相似。代码如下:

public static void test(File file){
        FileOutputStream fos = null;//字节流
        OutputStreamWriter osw = null;//转换
        BufferedWriter bw = null;//字符流
        //创建字节输出流
        try{
            fos = new FileOutputStream("/home/zeng/Desktop/debug.txt");//测试文件,中英文结合,用UTF-16LE进行编码
            //利用OutputStreamWriter来包装FileOutputStream字节流,并指定字节转换为字符的编码为UTF-16LE
            osw = new OutputStreamWriter(fos,"UTF-16LE");
            //利用BufferedWriter包装OutputStreamWriter
            bw = new BufferedWriter(osw);
            bw.write("abc测试字符串abc");
        }catch(Exception e){
            e.printStackTrace();
        }finally {
            if(bw!=null){
                try{
                    bw.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
            if(osw!=null){
                try{
                    osw.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
            if(fos!=null){
                try{
                    fos.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
        }
    }

7.如何读文件,写文件?

1.读文件

字节流文件读取:

public static void testBufferedInputStream() throws Exception{
        int tmp = -1;
        FileInputStream fis = new FileInputStream("/home/zeng/Desktop/test.png");
        BufferedInputStream bis = new BufferedInputStream(fis);
        while((tmp=bis.read())!=-1){
            System.out.println(tmp);
        }
        bis.close();
        fis.close();
    }

纯文本文件,例如txt,log,csv等格式,用下列代码读取,还能够指定编码格式:

public static void test(File file){
        FileInputStream fis = null;//字节流
        InputStreamReader isr = null;//转换
        BufferedReader br = null;//字符流
        //创建字节输出流
        try{
            fis = new FileInputStream("/home/zeng/Desktop/debug.txt");//测试文件,中英文结合,用UTF-16LE进行编码
            //利用InputStreamReader来包装FileInputStream字节流,并指定字节转换为字符的编码为UTF-16LE
            isr = new InputStreamReader(fis,"UTF-16LE");//正确
//            isr = new InputStreamReader(fis,"UTF-8");//乱码
            //利用BufferedReader包装InputStreamReader
            br = new BufferedReader(isr);
            //读取文件中的一行字符
            String s = null;
            while((s=br.readLine())!=null){
                System.out.println(s);
            }
        }catch(Exception e){
            e.printStackTrace();
        }finally {
            if(br!=null){
                try{
                    br.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
            if(isr!=null){
                try{
                    isr.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
            if(fis!=null){
                try{
                    fis.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
        }
    }

写文件同理。

8.如何实现文件和文件夹的复制?

1.文件复制

代码:

public static void copyFile(String oldPath,String newPath){
        try{
            int byteread = 0;
            File file = new File(oldPath);
            if(file.exists()){
                InputStream is = new FileInputStream(oldPath);
                FileOutputStream fos = new FileOutputStream(newPath);
                byte[] buffer = new byte[1024];
                while((byteread=is.read(buffer))!=-1){
                    fos.write(buffer,0,byteread);
                }
                is.close();
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

2.文件夹复制

public static void copyFolder(String oldPath,String newPath){
        try{
            //如果文件夹不存在则建立新文件夹
            (new File(newPath)).mkdirs();
            File a = new File(oldPath);
            String[] files = a.list();
            File temp = null;
            for (int i = 0; i < files.length; i++) {
                if(oldPath.endsWith(File.separator)){
                    temp = new File(oldPath+files[i]);
                }else{
                    temp = new File(oldPath+File.separator+files[i]);
                }
                if(temp.isFile()){
                    copyFile(oldPath+"/"+temp.getName().toString(),newPath+"/"+temp.getName().toString());
                }else{
                    copyFolder(oldPath+"/"+files[i],newPath+"/"+files[i]);
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

9.如何在文件的任意位置进行读写?

RandomAccessFile类是一种特殊的文件流,它同时实现了DataInput和DataOutput接口,因此可以用它来读写文件。RandomAccessFile通过一个文件指针来指定读写的位置,默认该指针处于文件开始处。该文件指针可以通过getFilePointer()方法读取,并通过seek()方法设置,从而实现在任何位置的读取或写入数据。

写操作示例:

public static void testWrite() throws Exception{
        RandomAccessFile rf = new RandomAccessFile("/home/zeng/Desktop/debug.txt","rw");
//        rf.seek(rf.length());//在末尾添加
        rf.writeByte('a');
        rf.writeBoolean(true);
        rf.writeUTF("测试");
        rf.close();
    }

读操作示例:

public static void testRead() throws Exception{
        RandomAccessFile rf = new RandomAccessFile("/home/zeng/Desktop/debug.txt","r");
        System.out.println((char)rf.read());
        System.out.println(rf.readBoolean());
        System.out.println(rf.readUTF());
        rf.close();
    }

10.使用Buffered缓冲流写文件,为什么内容没有写入?

在OutputStream和Writer输出流中,都提供了flush()和close()两个方法。flush()方法用于刷新输出流并强制写出所有缓冲的输出的字节。close()方法用于关闭输出流并释放与此流有关的所有系统资源。

在使用缓冲流写文件时,是将信息先写入缓冲流,如果不显式调用flush()和close()方法,则无法将缓冲区的信息输出到目标文件中,因此文件为空。正确代码如下:

public static void testBuffer()throws Exception{
        FileWriter fw = new FileWriter("/home/zeng/Desktop/debug.txt");
        BufferedWriter bw = new BufferedWriter(fw);
        bw.write('a');
        bw.write("大家好");
        bw.close();//必须调用才能正确写入
        fw.close();
    }

11.什么是NIO?与IO有什么区别和联系?

原有的IO操作都是以字节为单位进行读写的,虽然应用时使用了很多高级流进行了封装,不需要直接去处理字节流,但是底层的实现还是离不开字节处理。

原有的IO操作是一次一个字节的处理数据,速度比较慢;此外InputStream中的read()是一种阻塞性方法,该方法可用于从流中读取数据,但是如果数据源没有数据,它将一直等待,其他程序也不能执行。

为了解决原有IO中的一些问题,从JDK1.4提供了一系列改进IO处理的新特性,称为NIO(New IO).

NIO与原有的IO有同样的作用和目的,是基于原有IO的改进和扩展。

NIO与原有IO不同,它是基于特殊缓冲区块(buffer)进行的高效的IO操作。NIO的缓冲区块与普通的缓冲区不同,它是一块连续的空间,它内存的分配不在java的堆栈中,不受java内存回收的影响:它的实现不是纯java代码,而是本地代码,这样操作系统可以直接与缓冲区进行交互,java程序只需要完成对缓冲区的读写,而后续操作由操作系统完成。

异步通道Channel是NIO的另外一个重要的新特点
Channel并不是对原有IO类的扩充和完善,而是完全崭新的实现。通过Channel,java应用程序能够更好地与操作系统的IO服务结合起来,充分利用buffer缓冲区,完成高性能的IO操作。Channel的实现也不是纯java的,而是和操作系统结合紧密的本地代码。

NIO是对IO的改革,因为大量借助了本地代码,所以效率和性能都更快,但对操作系统和硬件平台有很大的依赖性,这将影响java的可移植性。

12.如何使用NIO读写文件?

读操作示例:

public static void fileReader(){
        String fileContent = null;
        FileInputStream fis = null;
        FileChannel fc = null;
        try{
            fis = new FileInputStream("/home/zeng/Desktop/debug.txt");
            //获取通道channel
            fc = fis.getChannel();
            //创建bytebuffer缓冲区,大小根据实际情况而定
            ByteBuffer bb = ByteBuffer.allocate(10000);
            fc.read(bb);
            bb.flip();
            fileContent = new String(bb.array());
        }catch(Exception e){
            e.printStackTrace();
        }finally {
            //释放fileChannel资源
            try{
                fc.close();
            }catch (Exception e){

            }try{
                fis.close();
            }catch (Exception e){

            }
        }
        System.out.println(fileContent);
    }

写操作示例:

public static void fileWriter(){
        String fileContent = "abc大家好123abc";
        FileOutputStream fos = null;
        FileChannel fc = null;
        byte[] bts = fileContent.getBytes();
        try{
            fos = new FileOutputStream("/home/zeng/Desktop/debug.txt");
            //获取通道channel
            fc = fos.getChannel();
            //创建bytebuffer缓冲区,大小根据实际情况而定
            ByteBuffer bb = ByteBuffer.allocate(bts.length);
            bb.put(bts);
            bb.flip();
            fc.write(bb);
        }catch(Exception e){
            e.printStackTrace();
        }finally {
            //释放fileChannel资源
            try{
                fc.close();
            }catch (Exception e){

            }try{
                fos.close();
            }catch (Exception e){

            }
        }
        System.out.println(fileContent);
    }

文件复制示例:

public static void fileWriter(){
        String fileContent = "abc大家好123abc";
        FileOutputStream fos = null;
        FileChannel fc = null;
        byte[] bts = fileContent.getBytes();
        try{
            fos = new FileOutputStream("/home/zeng/Desktop/debug.txt");
            //获取通道channel
            fc = fos.getChannel();
            //创建bytebuffer缓冲区,大小根据实际情况而定
            ByteBuffer bb = ByteBuffer.allocate(bts.length);
            bb.put(bts);
            bb.flip();
            fc.write(bb);
        }catch(Exception e){
            e.printStackTrace();
        }finally {
            //释放fileChannel资源
            try{
                fc.close();
            }catch (Exception e){

            }try{
                fos.close();
            }catch (Exception e){

            }
        }
        System.out.println(fileContent);
    }

    public static void copy(String srcFile,String destFile)throws Exception{
        ByteBuffer bb = ByteBuffer.allocate(10000);
        //获取NIO读取通道
        FileInputStream fis = new FileInputStream(srcFile);
        FileChannel in = fis.getChannel();
        //获取NIO写入通道
        FileOutputStream fos = new FileOutputStream(destFile);
        FileChannel out = fos.getChannel();

        int len = -1;
        //当读文件到末尾时结束循环
        while((len = in.read(bb))!=-1){
            //在write之前将position和limit设置好
            bb.flip();
            //按设定的position位置开始读,到limit结束
            out.write(bb);
            //初始化position/limit/capacity标志的位置,为下一次循环读取做准备
            bb.clear();
        }
        out.close();
        in.close();
        fos.close();
        fis.close();
    }

如前所述:java程序只需要完成对缓冲区的读写,而后续操作由操作系统完成。

注意:使用NIO读写文件,首先还需要使用IO流类。注意并不是所有的IO流类都支持NIO操作的,支持NIO操作的类有FileInputStream,FileOutputStream,RandomAccessFile.

13.什么是字符编码和解码?

在字符和字节信息相互转换时,需要涉及编码和解码操作
在NIO技术中提供了与字符集,编码和解码相关的API,存放在java.nio.charset包中,具体有:

1.Charset,字符集,用于描述字符和字节之间的命名映射关系
2.CharsetDecoder,解码器,用于实现将字节解码为字符
3.CharsetEncoder,编码器,用于实现将字符编码为字节

14.读写文件时为什么中文字符经常产生乱码?

存储和读取的过程示意图:
这里写图片描述

字符集用于描述字符和二进制之间的映射关系。如果编码和解码遵循相同的字符集,文件信息能正确显示,如果不同,将会产生乱码。

在文件读写时,除了由于字符集不一致引起乱码之外,API使用不当也会引起中文乱码

例如,使用InputStream读取中文字符的文件,由于中文字符占用两个字节,而InputStream是以字节为单位读取的,所有会将中文拆成两个字节分两次读取,进而出错。

15.如何解决FileReader读文件乱码问题?

一般情况,如果文件中包含中文字符,为了避免中文乱码问题,开发者会习惯选用FileReader类读文件。但是有些情况,即使是该类也会出错,原因是文件编码跟系统默认编码不一致。

事实上,FileReader属于字符流,是读取字符文件的便捷类。FileReader继承自InputStreamReader,前面讲过,InputStreamReader是将字节流转换为字符流的桥梁。FileReader也是借助InputStreamReader完成的转换,字符集用的是系统默认字符集。所以如果文件编码跟系统编码不一样,当然会出错。

解决办法:放弃使用FileReader,选用InputStreamReader,在获取InputStreamReader对象时显式地指定合适的字符集。

16.为什么DataInputStream和DataOutputStream读写文件时乱码?

使用上述两类,开发者可以以基本类型为单位对文件进行读写。使用DataOutputStream写入文件中的信息,除了包含信息本身之外,还有类型标识等,只有使用DataInputStream才能正确读取原有信息,其他方式会产生乱码,这也是使用记事本打开此类文件时显式乱码的原因。

注意:这两类在读写文件时需要配合使用,不能单独应用,否则读写过程将会产生乱码。

17.如何实现文件锁定功能?

java提供了两种方式:

1.使用RandomAccessFile类进行锁定。如下:

RandomAccessFile rf = new RandomAccessFile(file,"rws");

“s”代表同步方式,也就是锁。

2.使用FileChannel进行锁定。如下:

public static void lock()throws Exception{
        RandomAccessFile rf = new RandomAccessFile(new File(file),"rw");
        FileChannel fc = rf.getChannel();
        //尝试获取文件锁,如果有用户在使用将返回null
        FileLock fl = fc.tryLock();
        if(fl.isValid()){
            //允许读写操作
            //操作代码
            fl.release();
        }else{
            //不允许读写操作
        }
    }

18.如何实现对文件和目录的压缩,解压缩?

在使用zip格式压缩,解压缩时,压缩可以使用ZipEntry类和ZipOutputStream类,解压缩可以使用ZipEntry类和ZipInputStream类。

19.如何读写properties文件?

properties文件是java程序特有的一种文件类型,该文件的信息是以“键-值”格式存放。通常可以将容易变换的一些系统参数写到properties文件中,可以提高程序灵活性。示例如下:

name=tom
pwd=123456

第六章

1.线程,进程和程序之间有何区别和联系?

1.什么是线程

线程是进程的一个实体,是CPU调度和分配的基本单位,其本身不拥有系统资源,只含有程序计数器,寄存器和栈等一些运行时必不可少的基本资源。它的存在是为进程服务的,同属一个进程的线程共享进程所拥有的全部资源。

2.什么是进程

进程是具有一定独立功能的程序块关于某个数据集合上的一次运行活动,它是系统进行资源调度分配的一个独立单位。

3.什么是程序

程序是一组指令的集合,由多个进程功能完成,它是一个静态的实体,没有执行的含义。

4.线程和进程之间的区别

1.线程是进程的一部分,因此线程有时候被称为轻权进程或者轻量级进程。

2.一个没有线程的进程可以看做是单线程的,如果一个进程内拥有多个线程,那么进程的执行就不是一条线(线程),而是很多条线(线程)。

3.系统在运行时会为每个进程分配不同的内存区域,但是不会为线程分配内存。

4.进程是系统所有资源分配时候的一个基本单位,拥有一个完成的虚拟空间地址,并不依赖线程而独立存在。

第七章

1.什么是TCP/IP?什么是IP?

TCP/IP(Transmission Control Protocol/Internet Protocol),即传输控制协议/网际互联协议,它是Internet国际互联网络的基础。

TCP/IP通常被看成是一个4层模型,分为应用层,传输层,网络层以及网络接口层。具体如下:

1.应用层

例如web访问用到了HTTP,文件传输用FTP。电子邮件用SMTP,域名解析用DNS协议,远程登录用Telnet协议等,都属于TCP/IP应用层。

2.传输层

TCP/IP协议族在这一层协议有TCP和UDP。

3.网络层

网络层是TCP/IP协议族中非常关键的一层,负责相邻计算机之间的通信,主要定义了IP地址格式,从而能够使得不同应用类型的数据在Internet上顺畅地传输,IP协议就是一个网络层协议。

4.网络接口层

网络接口是TCP/IP软件的最底层,负责接收IP数据包并通过网络进行发送,或者从网络上接收物理帧,抽出IP数据报,交给IP层。

2.什么是HTTP?HTTP的工作原理如何?

HTPP(Hypertext Transfer Protocol)被称为超文本传输协议,用于控制万维网服务器和本地浏览器之间的超文本信息传送。该协议可以使浏览器访问速率更加高效,减少网络传输。它不仅保证计算机正确快速地传输超文本文档,还确定传输文档中的哪一部分,以及哪部分内容首先显示等等。

HTTP工作原理

从用户在浏览器输入URL到网页显示,HTTP的工作主要经历以下几个过程。

1.发送请求

用户输入URL地址后,客户机与服务器建立连接,客户机通过HTTP向web服务器发送请求,请求格式为统一资源标识符(URL),协议版本号,后边是MIME信息,包括请求修饰符,客户端信息和其他可能的内容。

2.响应请求

web服务器在接收到用户请求后,将请求资源通过HTTP发送给客户端浏览器,响应信息的格式为信息的协议版本号,一个成功或错误的代码,后边是MIME信息,包括服务器信息,实体信息和其他可能的内容。

3.处理响应

客户端接收服务器返回的响应信息,通过浏览器解析并显示,然后客户机与服务器断开连接

3.在Socket通信时如何获取主机和客户机的IP地址?

java提供了一个java.net.InetAddress类,该类用于表示互联网协议(IP)地址。代码如下:

public class Test {
    public static void main(String[] args){
        System.out.println(getLocalHostIp());
        System.out.println(getLocalHostName());
        String[] result1 = getAllLocalHostIp();
        for (int i = 0; i < result1.length; i++) {
            System.out.println(result1[i]);
        }
        String[] result2 = getAllLocalHostIpByName("www.baidu.com");
        for (int i = 0; i < result2.length; i++) {
            System.out.println(result2[i]);
        }
    }
    //获取本机IP地址
    public static String getLocalHostIp(){
        String ip;
        try{
            InetAddress addr = InetAddress.getLocalHost();
            ip = addr.getHostAddress();
        }catch(Exception e){
            ip = "";
        }
        return ip;
    }

    //获取本机名称
    public static String getLocalHostName(){
        String ip;
        try{
            InetAddress addr = InetAddress.getLocalHost();
            ip = addr.getHostName();
        }catch(Exception e){
            ip = "";
        }
        return ip;
    }
    //获取本机所有IP地址
    public static String[] getAllLocalHostIp(){
        String[] ret = null;
        try{
            String hostName = getLocalHostName();
            InetAddress[] addrs = InetAddress.getAllByName(hostName);
            if(addrs.length>0){
                ret = new String[addrs.length];
                for (int i = 0; i < addrs.length; i++) {
                    ret[i] = addrs[i].getHostAddress();
                }
            }
        }catch (Exception e){
            ret = null;
        }
        return ret;
    }

    //根据本机名获取其对应的所有IP地址
    public static String[] getAllLocalHostIpByName(String hostName){
        String[] ret = null;
        try{
            InetAddress[] addrs = InetAddress.getAllByName(hostName);
            if(addrs.length>0){
                ret = new String[addrs.length];
                for (int i = 0; i < addrs.length; i++) {
                    ret[i] = addrs[i].getHostAddress();
                }
            }
        }catch (Exception e){
            ret = null;
        }
        return ret;
    }
}

输出结果:

127.0.1.1
zeng-pc
127.0.1.1
180.97.33.107
180.97.33.108

4.如何获取Internet资源的大小?并下载资源

获取网络中资源文件的大小,需要使用java.net包下的URL类和URLConnection类。java.net.URL类代表一个统一资源定位符,它是指向互联网“资源”的指针。java.net.URLConnection类代表应用程序和URL之间的通信连接。通常,创建一个到URL的连接需要几个步骤:

1.通过在URL上调用openConnection()方法创建连接对象
2.设置操作参数和一般请求属性
3.使用connect()方法建立到远程对象的实际连接
4.远程对象变为可用,远程对象的头字段和内容变为可访问

实现获取网络资源大小的功能,代码如下:

public static void main(String[] args){
        try{
            URL url = new URL("https://y.gtimg.cn/music/photo_new/T001R300x300M0000044wQXL0ElWF1.jpg?max_age=2592000");
            URLConnection conn = url.openConnection();
            conn.connect();
            //文件大小,单位字节
            System.out.println(conn.getContentLength());
            //下载文件
            InputStream is = conn.getInputStream();
            FileOutputStream fos = new FileOutputStream("/home/zeng/Desktop/zhangyu.png");
            byte[] buf = new byte[1024];
            int size = -1;
            while((size=is.read(buf))!=-1){
                fos.write(buf,0,size);
            }
            fos.close();
            is.close();
            conn.connect();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

5.如何实现Internet资源下载的断点续传?

断电续传原理

在发送HTTP请求时,可以设置一个“RANGE”参数,该参数用于指定访问资源的字节区间。例如”RANGE:bytes=10000-20000”的意思是告诉服务器将目标资源中10000-20000字节之间的内容返回。

断点续传的原理就是在每次发送请求时,将上次断点的位置通过”RANGE”参数设定,实现资源的继续访问。

6.如何实现Internet资源的多线程下载?

多线程下载的示例由MultiDownload和DownThread两个类构成。

1.DownThread

MultiDownload类是一个下载线程,该线程在启动时需要指定下载文件的URL,下载起始点和下载结束点等参数。下载过程中利用RandomAccessFile类将下载的内容写入目标文件,其中的seek()方法可以指定写入目标文件的起始位置。

2.MultiDownload

MultiDownload类主要功能是获取下载文件的大小,并根据指定的线程数量计算出每一个线程应该下载的起始点和结束点,然后创建并启动线程。

第八章

1.如何过滤字符串前后以及中间出现的空格?

当只需要对字符串前后出现的空格进行过滤时,只需要通过String类中提供的trim()方法即可实现。该方法返回字符串对象的一个副本,忽略前导和尾部空格。如果此字符串对象表示一个空的字符串序列或者该字符串的前导和尾部都没有空格出现,则返回该字符串本身。

对于需要对中间的空格或者其他特殊字符过滤时可用一下方法:

1.使用字符串替换(string = string.replaceAll(" ",""))
2.使用StringTokenizer类

使用replaceAll()方法最为简单,使用StringTokenizer类效率最高。

2.如何判断一个字符串是否符合数值格式?

利用正则表达式:

String input = "12345";
Pattern p = Pattern.compile("\\d*");
Matcher m = p.matcher(input);
System.out.println(m.matches());

3.如何实现字符串的切割与查找?

字符串切割

1.使用String类的subString()方法
2.使用String类的split()方法
3.使用StringTokenizer类(与split()类似)
4.使用正则表达式

字符串分析

1.使用String类的indexOf()方法
2.使用正则表达式

因为StringTokenizer类是出于兼容性原因才被保留下来,所以不推荐使用。实现复杂功能请使用正则表达式。

4.十进制与二进制之间的相互转换?

十进制转换为二进制

Integer.toBinaryString(int i)方法以二进制无符号整数形式返回一个int类型整数参数的字符串序列。如果需要返回数值类型,只需要调用Integer或者Double类中的parseInt()或parseDouble()方法即可。

二进制转换为十进制

Integer类中提供的parseInt(String s,int radix)方法,返回字符串序列以指定进制表示的整数表示形式。

示例代码:

public static void main(String[] args){
        int i = 111;//变为-111就会出错
        String str = Integer.toBinaryString(i);
        Integer in = Integer.parseInt(str);
        System.out.println(str);
        System.out.println(in);
        int no = Integer.parseInt(str,2);
        System.out.println(no);
    }

5.如何将字节流转换为指定编码的字符串?

代码如下:

public static void main(String[] args) throws UnsupportedEncodingException {
        String str = "字符串编码转换";
        byte[] bt = str.getBytes();
        String str2 = new String(bt,"utf-8");//正常显示
//        String str2 = new String(bt,"GBK");//乱码显示
        System.out.println(str2);
    }

6.String,StringBuffer,StringBuilder有什么区别?

StringBuilder类是StringBuffer类的一个等价类,该类与StringBuffer类具有相同的方法,且同样代表的是可变长的字符串缓冲区,不同地方在于StringBuilder类是非线程安全的。但正是因为少了很多的同步操作,在效率上会高于StringBuffer类。

7.什么是反射机制?有什么作用?

反射,主要是指程序可以访问,检测和修改其自身状态或行为的一种能力。

在java环境中,反射机制允许程序在执行时获取某个类自身的定义信息,例如属性和方法等也可以实现动态创建类的对象,变更属性的内容或执行特定的方法的功能。从而使java具有动态语言的特性,增强了程序的灵活性和可移植性。

java反射机制主要用于实现以下功能:

1.在运行时判断任意一个对象所属的类型
2.在运行时构造任意一个类的对象
3.在运行时判断任意一个类所具有的成员变量和方法
4.在运行时调用任意一个对象的方法,甚至可以调用private方法

注意:上述功能逗是在运行时环境中,而不是在编译时环境中

实现java反射机制的API在java.lang.reflect包下,具有以下几点:

1.Class类:代表一个类
2.Field类:代表类的成员变量
3.Method类:代表类的方法
4.Constructor类:代表类的构造方法
5.Array类:提供了动态创建数组以及访问数组的元素的静态方法。该类中的所有方法都是静态的。

示例代码,仔细看看:

//Test.java
public class Test {
    private static Student student;
    public static void main(String[] args) throws Exception {
//        test1();
        test2();
        test3(student,"id","8954");
        test4();
    }
    //取得所有属性和方法
    public static void test1(){
        Class c = String.class;
        Method[] methods = c.getMethods();
        for (int i = 0; i < methods.length; i++) {
            System.out.println(methods[i].getName());
        }
        Field[] fields = c.getFields();
        for (Field each:
             fields) {
            System.out.println(each.getType()+": "+each.getName());
        }
    }
    //根据类名动态创建类的对象
    public static void test2() throws Exception {
        Class c = Class.forName("Student");
        student = (Student)c.newInstance();
        student.setName("java");
        student.setId("1001");
        student.show();
    }
    //根据类名和方法名,执行对象的方法
    //执行student对象指定名称的方法
    public static void test3(Student student,String method,String value) throws Exception {
        String s1 = method.substring(0,1).toUpperCase();
        String s2 = method.substring(1);
        String m = "set"+s1+s2;
        System.out.println(m);
        Class c = student.getClass();
        Method set = c.getMethod(m,new Class[]{String.class});
        set.invoke(student,new Object[]{value});
    }
    //动态创建数组对象,对数组元素赋值和取值
    public static void test4() throws Exception {
        Class cls = Class.forName("java.lang.String");
        //创建一个String类型的数组,大小为10
        Object arr = Array.newInstance(cls,10);
        //在数组5索引位置赋值
        Array.set(arr,5,"this is a test");
        //获取数组5索引位置的值
        String s = (String) Array.get(arr,5);
        System.out.println(s);
    }
}

//Student.java
public class Student {
    private String id;
    private String name;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    public void show(){
        System.out.println(id+" "+name);
    }
}

输出:

1001 java
setId
this is a test

java语言反射提供了一种动态链接程序组件的多功能方法。它允许程序创建和控制任何类的对象,无需提前硬编码目标类。这些特性使得反射特别适用于创建以普通方式与对象协作的库。

反射在性能方面会有一定的损耗,用于字段和方法接入时反射要远慢于直接代码。

流行的框架如SSH都是基于反射机制实现的,首先需要将XML配置文件中的配置信息读取,然后利用反射机制创建对象,执行方法等。

8.如何获取当前工程目录?

1.获取class路径的代码如下:

public static void main(String[] args) throws Exception {
        System.out.println(Thread.currentThread().getContextClassLoader().getResource(""));
        System.out.println(Test.class.getClassLoader().getResource(""));
        System.out.println(ClassLoader.getSystemResource(""));
        System.out.println(Test.class.getResource(""));
        System.out.println(Test.class.getResource("/"));
    }

2.获取工程路径:

public static void main(String[] args) throws Exception {
        System.out.println(new File("").getAbsolutePath());
        System.out.println(System.getProperty("user.dir"));
    }

9.如何使用java调用系统的exe文件?

每个java程序都有一个Runtime类实例,使程序能够与其运行的环境相连接。示例代码:

public static void main(String[] args) throws Exception {
        //获得一个Runtime对象实例
        Runtime m = Runtime.getRuntime();
        //执行
        m.exec("D:\\qq.exe");
    }

10.如何使用java执行cmd命令?

通过java程序执行cmd命令需要使用Runtime类和Process类。Runtime类能够使应用程序与其运行环境相连接。Process可用于控制进程并获取相关信息。Process类提供了进程输入,进程输出,等待进程完成,检查进程的退出状态以及销毁进程的方法的功能。

第九章

1.什么是JDBC?有什么作用?

JDBC全称为java database connectivity standard,是java访问数据库的应用程序接口。

JDBC大部分API都采用接口形式设计,接口的实现类都交给数据库提供商完成,这样使得数据库访问不依赖特定类型的数据库,开发者可以利用统一方式操作各种数据库。

2.java与数据库的连接方式有哪些?

要使用JDBC连接某一特定的数据库,必须有和数据库相对应的驱动程序,驱动程序往往是由数据库的厂商提供的,是连接JDBC API和数据库之间的桥梁。驱动程序是适应某种特定数据库,实现JDBC接口的一组实现类。

在java访问数据库时,java程序首先使用DriverManager类载入指定的驱动程序,然后使用JDBC API与DriverManager类交互,完成数据库的增加,删除,修改和查询操作。

3.如何提升SQL语句的查询性能?

在对数据库进行操作时,如何SQL语句书写不当,对程序的效率也会造成很大影响。

提高SQL语句的执行效率可以从下面几个方面入手。

1.数据库设计与规划

1.primary key字段的长度尽量小,能用small integer就不要用integer。利用员工数据表,如果能用员工编号作为主键,就不要用身份证号码。

2.字符字段如果长度固定,就不要用varchar或nvarchar类型,而应该使用char或nchar类型。例如身份证号码,银行密码等。

3.字符字段如果长度不固定,则应该使用varchar或nvarchar类型。除了可节省存储空间外,存取硬盘时也会交由效率。例如地址,个人介绍等。

4.设计字段时,如果其值可有可无,最好也给一个默认值,并射程“不允许null”。因为有些数据库在存放和查询时有null的数据表时,会花费额外的运算动作。例如SQL Server数据库。

2.适当地创建索引

1.primary key字段可以自动创建索引,而foreign key字段不可以。为foreign key字段手动创建索引,即使是很少被join的数据表也有必要这样做。

2.为经常被查询或排序的字段创建索引。

3.索引字段的长度不宜过长,不要用超过20个字节的字段,例如地址等。

4.不要为内容重复性高的字段创建索引,例如性别等。

5.不要为使用率低的字段建立索引。

6.不宜为过多字段建立索引,否则会影响到insert,update,delete语句的性能。

7.如果数据表存放的数据很少,就不必刻意使用索引。

3.使用索引功能

在查询数据表时,使用索引查询可以极大提升查询速度,但是如果where字句书写不当,即使某些地方列存在索引,也不能使用该索引查询,而同样会使用全表扫描,这就造成查询速度的降低。在where语句中避免使用一下关键词:
not,!=,<>,!>,!<,exists,in,like,||。

使用like关键字做模糊查询时,即使已经为某个字段建立索引,但需以常量字符开头才会使用到索引,如果以”%”开头则不会使用索引。例如”name like ‘%to’”不启用name字段上的索引,而”name like’to%’”会启用name字段上的索引。

4.避免在where字句中对字段使用函数

5.and和or的使用

在and运算中,只要有一个条件使用到索引,即可大幅度提升查询速度。但在or运算中,则要所有的条件都有使用到索引才能提升查询速度,因此使用or运算符时要特别小心。

6.join与子查询

相对于子查询,如果能使用join完成的查询,一般建议使用后者。原因除了join的语法容易理解外,在多数情况下,join的性能也会比子查询高。

7.尽可能使用存储过程(sorted procedure)

sorted procedure除了经过事先编译,性能较好之外,也可减少SQL语句在网络中的传递,方便商业逻辑的重复使用。

8.尽可能在数据源过滤数据

使用select语法时,尽量先用SQL条件或sorted procedure过滤出所要的信息,避免将大量冗余数据返回给程序,然后由程序处理。

4.如何解决mysql数据库插入乱码的问题?

解决办法如下:

1.设置连接字符串编码

在数据库连接字符串后面追加参数,指明向mysql服务器发送SQL语句的编码格式,格式如下:

jdbc:nysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8

2.设置数据表及其字段的编码

第十章

1.什么是jsp?jsp的工作原理如何?

jsp是java server pages的简称,是sun公司在1999年推出的一种动态网页技术标准。

jsp程序的工作方式为请求/响应模式,客户端发出HTTP请求,jsp程序收到请求后进行处理,并返回处理的结果。

jsp程序需要运行于特定的web容器中,例如tomcat等。所有jsp文件在执行的时候都会被服务器端的jsp引擎转换为servlet程序(java源文件),然后调用java编译器将servlet程序编译为class文件(字节码文件),并由java虚拟机解释执行。如果已经存在class文件,则不需要编译,跟java源代码的过程是一样的。

2.jsp中有哪些内建对象?分别有什么作用?

在jsp页面中,提供了9中内建对象,分别如下:

1.request对象

request代表请求对象,主要用于接受客户端通过HTTP协议连接传输到服务器端的数据。

2.response对象

response代表响应对象,主要用于向客户端发送数据。

3.session对象

session代表客户端与服务器的会话,从客户连接到服务器开始,直到客户端与服务器断开连接为止。session对象主要用于保存用户信息。

4.out对象

out主要用于向客户端输出数据。

5.page对象

page对象代表当前jsp页面本身,有点类似于this关键字,它是java.lang.Object类的实例,平时很少使用。

6.application对象

application对象实现了用户间数据的共享,可存放全局变量。它开始于服务器的启动,直到服务器关闭。在此期间,此对象将一直存在。

7.exception对象

exception对象是一个异常对象,当一个页面在运行过程中发生了异常时,就产生该对象。exception是java.lang.Throwable类型的实例。

8.pageContext对象

pageContext对象提供了对jsp页面内所有的对象及名字空间的访问,使用它可以访问session,request和application等对象中的内容。

9.config对象

其中out,request,response,session是使用次数最多的,重点学习。

3.page,request,session和application有什么区别?

主要区别:

1.类型不同

2.作用范围不同

application:全局作用范围,整个应用程序共享。
session:生命周期为会话超时或者服务器端强制使会话失效。
request:请求作用域,客户端的一次请求。生命周期为一次请求或者使用forward方式执行请求转发。
page:一个jsp页面有效。

page,request,session,application对象作用范围是越来越大,request和page的生命周期都是短暂的,它们两个区别是:一个request可以包含多个page页(include,forward以及filter)

4.jsp中forward和redirect有什么区别?

使用forward由a.jsp跳转到b.jsp后,request请求对象使用的是同一个,因此可以在b.jsp中使用request对象获取前一次请求的客户信息,也可以利用request对象从a.jsp往b.jsp中传值。

因为request对象无法跨站点使用,因此forward方法无法实现跨站点跳转,需要实现跨站点跳转,可以使用java的redirect方式等。

redirect可实现服务器端请求的重定向,在实现redirect跳转过程中,浏览器又重新发出了一次重定向请求,服务器会将a.jsp使用的request对象销毁,并重新创建一个request对象传递给b.jsp。因此使用redirect跳转时,无法使用request对象从a.jsp往b.jsp中传值。

5.如何在多个jsp页面之间传递信息?

主要方式:

1.使用URL

在发送请求时,可以借助于URL进行传值,URL示例格式如下:

http://locolhost:8080/javafaq/hello.jsp?name=tom&age=11

获取上述URL中信息的代码如下:

String n = request.getParameter("name");
String a = requesr.getParameter("age");

2.使用request对象

在多个页面之间使用forward跳转时,可以使用request对象传值。任何类型的对象,在存入request对象后,都变为Object类型,因此使用getAttribute()方法获取时,需要类型强制转换。

3.使用session对象

使用session对象在多个页面间传值,是最常用的方法

4.使用application对象

在应用application时,需要考虑并发问题

适用:如果相邻页面传值,可以考虑用URL或request,如果是单用户跨多个页面传值,考虑用session,如果是多用户跨多个页面传值,考虑用application。

6.什么是session?如何使用session?

session对象存储于web服务器内存中,并与用户相关,不同用户具有不同的session对象,每个session对象的信息只能由特定用户使用。

设置session有效期

session对象存储于服务器内存中,用于跟踪存储用户信息。Session对象默认有效期一般为30分钟,如果用户在该时间内不做任何操作,服务器会将session对象自动销毁。可以在tomcat的conf/server.xml中定义修改服务器上所有程序的默认有效期。

7.如何在关闭页面时自动清除session?

方法如下:

1.使用removeAttribute()方法,如下:

session.setAttribute("name","tom");
session.removeAttribute("name");

2.使用invalidate()方法

可以清除session对象中所有信息,如下:

session.invalidate();

一般情况下,在关闭浏览器后,session信息需要等到session对象失效才能清除,如果需要实现关闭浏览器就清除session,可以通过onbeforeunload事件属性捕获关闭事件。

8.什么是cookie?如何使用cookie?

使用cookie,web服务器可以将一些客户的特定信息存储在客户计算机中,例如上次访问的位置,花费的时间或用户首选项等。cookie的信息应该是小量的,不影响系统安全性的信息,如用户名密码不建议写入。

cookie信息在浏览器运行时存储在客户计算机的内存中,如果客户从网站或web服务器中退出,cookie也可存储在客户计算机的磁盘上。

9.如何在禁用cookie的情况下使用session?

cookie和session之间有一定的关联,如果客户端禁用了cookie,session使用会受到影响。

cookie信息是以文本文件的的方式存储在客户端,而session信息却存储在服务器端内存,当客户发送请求时,cookie信息会随请求一起发送到服务器,服务器会根据cookie中的SessionID寻找客户对应的session对象,因此如果cookie被用户禁用后,会影响session的使用。

如果客户端禁用了cookie,可以使用URL重写,将sessionID添加到URL中,利用URL传递给服务器

10.如何实现网站登录记忆跳转的功能?

记忆登录跳转是指当用户访问系统受保护页面时,会将页面转移到登录页面,要求用户登录,当用户登录后,将页面转移到登录前要访问的页面。

使用request.getHeader("Referer") 可以方便地获取上次访问的URL请求,从而实现记忆跳转的功能,但是该方法不能处理POST方式提交的参数信息。

为了将上次请求的所有信息都保存下来,可以写一个工具类,将请求和表单信息拼接写成一个字符串,保存到表单中的一个隐藏域,等登录成功后,再将隐藏域信息获取并解析,然后发送请求完成页面的跳转。

–end

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值