Java 基础总结(三)

Java 基础知识总结

内部类

内部类分为:成员内部类,局部内部类以及匿名内部类。

每个内部类都能独立地继承一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。

接口只是解决了部分问题,而内部类使得多重继承的解决方案变得更加完整。

1、成员内部类

在一个类中使用内部类,可以随意使用外部类的成员方法以及成员变量(包括私有private的)。内部类的实例一定要绑在外部类的实例上,如果从外部类中初始化一个内部类对象,那么内部类对象就会绑定在外部类对象上。内部类初始化方式与其他类初始化方式相同。但内部类的成员只有在内部类的范围时可用,不能被外部类直接使用。但是可以使用内部类对象引用调用内部类成员变量。

成员内部类是依附外部类而存在的,也就是说,如果要创建成员内部类的对象,前提是必须存在一个外部类的对象。

 //第一种方式:
 Outter outter = new Outter();
 Outter.Inner inner = outter.new Inner();  //必须通过Outter对象来创建
         
 //第二种方式:
 Outter.Inner inner1 = outter.getInnerInstance();

注意:

  1. 成员内部类中不能存在任何static的变量和方法
  2. 成员内部类是依附于外围类的,所以只有先创建了外围类才能够创建内部类。
  3. 当成员内部类拥有和外部类同名的成员变量或者方法时,会发生隐藏现象,即默认情况下访问的是成员内部类的成员。如果要访问外部类的同名成员,需要以下面的形式进行访问:外部类.this.成员变量、外部类.this.成员方法

2、局部内部类

内部类可以在类的局部位置定义,如在类的方法或任意的作用域中均可定义内部类。它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内。

注意:局部内部类就像是方法里面的一个局部变量一样,是不能有public、protected、private以及static修饰符的。

3、匿名内部类

匿名内部类应该是平时我们编写代码时用得最多的,在编写事件监听的代码时使用匿名内部类不但方便,而且使代码更加容易维护。由于匿名内部类没有名称,所以匿名内部类使用默认的构造方法来生成内部类对象,在匿名内部类定义结束后,需要加分号标识,这个分号并不代表定义内部类结束的标识,而是代表创建内部类引用表达式的标识。

这种写法虽然能达到一样的效果,但是既冗长又难以维护,所以一般使用匿名内部类的方法来编写事件监听代码。同样的,匿名内部类也是不能有访问修饰符和static修饰符的。

匿名内部类是唯一一种没有构造器的类。正因为其没有构造器,所以匿名内部类的使用范围非常有限,大部分匿名内部类用于接口回调。一般来说,匿名内部类用于继承其他类或是实现接口,并不需要增加额外的方法,只是对继承方法的实现或是重写。

说明:匿名内部类编译以后,会产生以“外部类名$序号”为名称的.class文件,序号以1-n排列,分别代表1-n个匿名内部类。

注意:

  1. 匿名内部类是没有访问修饰符的
  2. new 匿名内部类,这个类首先是要存在的。如果我们将那个InnerClass接口注释掉,就会出现编译出错
  3. 当所在方法的形参需要被匿名内部类使用,那么这个形参就必须为final
  4.  匿名内部类是没有构造方法的。因为它连名字都没有何来构造方法

4、静态内部类

在内部类前添加static修饰符,这个内部类就变成静态内部类了。一个静态内部类中可以声明static成员,但是在非静态内部类中不可以声明静态成员。静态内部类有一个最大的特点,就是不可以使用外部成员的非静态成员,所以静态内部类在程序开发中很少见。

注意:

  1. 如果创建静态内部类的对象,不需要其外部类的对象。
  2. 不能从静态内部类的对象中访问非静态外部类对象。

5、内部类的继承

在某个类继承内部类时,必须硬性给予这个类一个带参数的构造方法,并且该构造方法的参数为需要继承内部类的外部类的引用,同时在构造方法体中使用a.super()语句,这样才为继续提供了必要的对象引用。

public class OutputInnerClass extends ClassA.ClassB {
    
    public OutputInnerClass(ClassA a){
        a.super();
    }

}

class ClassA {

    class ClassB {
    
    }

}

注意:

  1. 成员内部类的引用方式必须为 Outter.Inner.
  2. 造器中必须有指向外部类对象的引用,并通过这个引用调用super()。这段代码摘自《Java编程思想》

为什么成员内部类可以无条件访问外部类的成员?

编译器会默认为成员内部类添加了一个指向外部类对象的引用,虽然定义的内部类的构造器是无参构造器,编译器还是会默认添加一个参数,该参数的类型为指向外部类对象的一个引用,所以成员内部类中的Outter this&0 指针便指向了外部类对象,因此可以在成员内部类中随意访问外部类的成员。从这里也间接说明了成员内部类是依赖于外部类的,如果没有创建外部类的对象,则无法对Outter this&0引用进行初始化赋值,也就无法创建成员内部类的对象了。

静态内部类有特殊的地方吗?

静态内部类是不依赖于外部类的,也就说可以在不创建外部类对象的情况下创建内部类的对象。另外,静态内部类是不持有指向外部类对象的引用的,这个读者可以自己尝试反编译class文件看一下就知道了,是没有Outter this&0引用的。

内部类的使用场景和好处

  1. 每个内部类都能独立的继承一个接口的实现,所以无论外部类是否已经继承了某个(接口的)实现,对于内部类都没有影响。内部类使得多继承的解决方案变得完整,
  2. 方便将存在一定逻辑关系的类组织在一起,又可以对外界隐藏。
  3. 方便编写事件驱动程序
  4. 方便编写线程代码

Java反射机制

Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。

通过Java反射机制,可以在程序中访问已经装载到JVM中的Java对象的描述,实现访问、检测和修改描述Java对象本身信息的机制。Java反射机制的功能十分强大,在java.lang.reflect包中提供了对该功能的支持。

通过反射可访问的主要描述信息
组成部分 访问方法 返回值类型 说明
包路径 getPackage() Package对象 获得该类的存放路径
类名称 getName() String对象 获得该类的名称
继承类 getSuperClass() Class对象 获得该类继承的类
实现接口 getInterface(s) Class型数组 获得该类实现的所以接口
构造方法 getConstructors() Constructor型数组 获得所有权限为public的构造方法
getConstructors(Class...parameterTypes) Constructor对象 获得权限为public的指定构造方法
getDeclaredConstructors() Constructor型数组 获得所有构造方法,按声明顺序返回
getDeclaredConstructor(Class...parameterTypes) Constructor对象 获得指定构造方法
方法 getMethods() Method型数组 获得所有权限为public的方法
getMethod(String name,Class...parameterTypes) Method对象 获得权限为public的指定方法
getDeclaredMethods() Method型数组 获得所有方法,按声明顺序返回
getDeclaredMethod(String name,Class...parameterTypes) Method对象 获得指定方法
成员变量 getFields() Field型数组 获得所有权限为public的成员变量
getField(String name) Field对象 获得权限为public的指定成员变量
getDeclaredFields() Field型数组 获得所有成员变量,按声明顺序返回
getDeclaredField(String name) Field对象 获得指定成员变量
内部类 getClasses() Class型数组 获得所有权限为public的内部类
getDeclaredClasses() Class型数组 获得所有内部类
内部类的声明类 getDeclaredClass() Class对象 如果该类是内部类,则返回它的成员类,否则返回null

说明:在通过getFields()和getMethods()方法依次获得权限为public的成员变量和方法时,将包含从超类中继承到的成员变量和方法;而通过方法getDeclaredFields()和getDeclaredMethods()只获得在本类中定义的所有成员变量和方法。

访问构造方法

在通过下列一组方法访问构造方法时,将返回Constructor类型数组或对象。每个Constructor对象代表一个构造方法,利用Constructor对象可以操作相应的构造方法。

  1. getConstructors()
  2. getConstructors(Class...parameterTypes)
  3. getDeclaredConstructors()
  4. getDeclaredConstructor(Class...parameterTypes)

如果是访问指定的构造方法,需要根据该构造方法的入口参数的类型来访问。例如,访问一个入口参数类型依次为String和int型的构造方法,通过下面两种方式实现:

  1. objectClass.getDeclaredConstructor(String.class,int.class);
  2. objectClass.getDeclaredConstructor(new Class{String.class,int.class});
Constructor类常用方法
方法 说明
isVarArgs() 查看该构造方法是否允许带有可变数量的参数,如果允许返回true,否则返回false
getParamentTypes() 按照声明顺序以Class数组形式获得该构造方法的各个参数类型
getExceptionTypes() 以Class数组的形式获得该构造方法可能抛出的异常类型
newInstance(Object ...initargs) 通过该构造方法利用指定参数创建一个该类的对象,如果未设置参数则表示采用默认无参数d的构造方法
setAccessible(boolean flag) 如果该构造方法的权限为private,默认为不允许通过反射利用newInstance(Object ...initargs)方法创建对象。如果执行该方法,并将参入口参数设置为true,则允许创建
getModifiers() 活得可以解析出该构造方法所采用修饰符的整数

通过java.lang.reflect.Modifier类可以解析出getModifer()方法的返回值所表示的修饰符信息,在该类中提供了一系列用来解析的静态方法,既可以查看是否被指定的修饰符修饰,还可以以字符串的形式获得所有修饰符。

Modifier类中的常用解析方法
静态方法 说明
isPublic(int mod) 查看是否被public修饰符修饰,如果是则返回true,否则返回false
isProtected(int mod) 查看是否被protected修饰符修饰,如果是则返回true,否则返回false
isPrivate(int mod) 查看是否被private修饰符修饰,如果是则返回true,否则返回false
isStatic(int mod) 查看是否被static修饰符修饰,如果是则返回true,否则返回false
isFinal(int mod) 查看是否被final修饰符修饰,如果是则返回true,否则返回false
toString(int mod) 以字符串的形式返回所有修饰符

访问成员变量

在通过下列一组方法访问成员变量时,将返回Field类型的对象或数组。每个Field对象代表一个成员变量,利用Field对象可以操作相应的成员变量。

  1. getFields()
  2. getField(String name)
  3. getDeclaredFields()
  4. getDeclaredField(String name)
Filed类的常用方法
方法 说明

getName()

获得该成员变量名称
getType() 获得表示该成员变量类型的Class对象
get(Object obj) 获得指定对象obj中成员变量的值,返回值为Object类型
set(Object obj,Object value) 将指定对象obj中成员变量值设置为value
getInt(Object obj) 获得指定对象obj中类型为int的成员变量值
setInt(Object obj,int i) 将指定对象obj中类型为int的成员变量值设置为i
getFloat(Object obj) 获得指定对象obj中类型为float的成员变量值
getFloat(Object obj,float f) 将指定对象obj中类型为float的成员变量值设置为f
getBoolen(Object obj) 获得指定对象obj中类型为boolean的成员变量值
setBoolen(Object obj,boolean z) 将指定对象obj中类型为boolean的成员变量值设置为z
setAccessible(boolean flag) 此方法可以设置是否忽略权限限制直接访问private等私有权限的成员变量
getModifiers() 获得可以解析出该成员变量所采用修饰符的整数

访问方法

再通过下列一组方法访问时,将返回Method类型的对象或数组。每个Method对象代表一个方法,利用Method对象可以操作相应的方法。

  1. getMethods()
  2. getConstructors(Class...parameterTypes)
  3. getDeclaredMethods()
  4. getDeclaredMethod(String name,Class...parameterTypes)

如果是访问指定的方法,需要根据该方法的名称和入口参数的类型来访问。例如,访问一个名称为print、入口参数型依次为String和int类型的方法,通过下面两种方式均可实现:

  1. objectClass.getDeclaredMethod("print",String.class,int.class);
  2. objectClass.getDeclaredMethod("print",new Class{String.class,int.class});
Method类常用方法
方法 说明
getName() 获得该方法名称
getParamentTypes() 按照声明顺序以Class数组的形式获得该方法的各个参数的类型
getReturnType() 以Class对象的形式获得该方法的返回值类型
getExceptionTypes() 以Class数组的形式获得该方法可能抛出的异常类型
invoke(Object obj,Object ... args) 利用指定参数args执行指定对象obj中的该方法,返回值为Objcet型
isVarArgs() 查看该构造方法是否允许带有可变数量的参数,如果允许返回true,否则返回false
getModifiers() 获得可以解析出该成员变量所采用修饰符的整数

注意:在反射中执行具有可变数量的参数的构造方法时,需要将入口参数定义成二维数组。

Annotation

Java中提供了Annotation功能,该功能用于类、构造方法、成员变量、方法、参数等的声明中。该功能并不影响程序的运行,但是会对编译器警告等辅助工具产生影响。

在定义Annotation类型时,也需要用到用来定义接口的interface关键字,但需要在interface关键字前加y一个“@”符号,即定义Annotation类型的关键字为@interface,这个关键字的隐含意思是继承了java.lang.annotation.Annotation接口。如下:

public interface OneMemberAnnotation{

    String value();

}

// String:成员类型。可用的成员类型有String、Class、primitive、enumerated和annotation,以及所列类型的数组
// value:成员名称。如果所在定义的Annotation类型中只包含一个成员,通常将成员名称命名为value

在为Annotation类型定义成员时,也可以设置默认值。

public interface OneMemberAnnotation{

    String value() default "<默认值>";

    Class type() default void.class;

}

在定义Annotation类型时,还可以通过Annotation类型@Target来设置Annotation类型适用的程序元素种类。如果未设置@Target,则表示适用于所有程序元素。枚举类型ElementType中的枚举常量用来设置@Target。

枚举类ElementType中的枚举常量
枚举常量 说明
ANNOTATION_TYPE 表示用于Annotation类型
TYPE 表示用于类、接口和枚举,以及Annotation类型
CONSTRUCTOR 表示用于构造方法
FIELD 表示用于成员变量和枚举类型
METHOD 表示用于方法
PARAMETER 表示用于参数
LOCAL_VARIABLE 表示用于局部变量
PACKAGE 表示用于包
SOURCE 表示不编译Annotation到文件类中,有效范围最小
CLASS 表示编译Annotation到文件类中,但是在运行时不加载Annotation到JVM中
RUNTIME 表示在运行时加载Annotation到JVM中,有效范围最大

例:

// 定义注释构造方法
import java.lang.annotation;

@Target(ElementType.CONSTRUCTOR)     // 用于构造方法
@Retention(RetentionPolice.RUNTIME)  // 在运行时加载Annotation到JVM中
public @interface Constructor_Annotation {

    String value() default "默认构造方法";
}

如果在定义Annotation类型时将@Retention设置为RetentionPolice.RUNTIME,那么在运行程序时通过反射既可以获取到相关Annotation信息,如获取构造方法、字段和方法的信息。

类Constructor、Fieldh和Method均继承了AccessibleObject类。

多线程

世间万物都可以同时完成很多工作,例如人体可以同时进行呼吸、血液循环、思考等活动,用户既可以使用计算机听歌,也可以使用它完成打印文件,而这些活动完全可以同时进行,这种思想放在Java中被称为并发,而将并发完成的每一件事情被称为线程。

在Java中,并发机制非常重要,但并不是所有的程序语言都支持线程。在以往的程序中,多以一个任务完成后在进行下一个项目的模式进行开发,这样下一个任务的开始必须等待前一个任务的结束。Java语言提供了并发机制,程序员可以在程序中执行多个线程,每一个线程完成一个功能,并于与其他线程并发执行,这种机制被称为多线程。

Java中的多线程在每个操作系统中的运行方式也存在差异,在此着重说明多线程在Windows操作系统中的运行模式。Windows操作系统是多任务操作系统,它以进程为单位。一个进程是一个包含又自身地址的程序,每个独立执行的程序都成为进程,也就是正在执行的程序。系统可以分配给没给进程一段有限的使用CPU的时间(也可以称为CPU时间片),CPU在这段时间中执行某个进程,然后下一个时间片又跳至另一个进程中去执行。由于CPU转换较快,所以使得每个进程好像是同时执行一样。

一个线程则是进程中的执行流程,一个进程中可以同时包括多个线程,每个线程也可以得到一小段程序的执行时间,这样一个进程就可以具有多个并发执行的线程。在单线程中,程序代码按调用顺序依次往下执行,如果需要一个进程同时完成多段代码的操作,就需要产生多线程。

在Java中主要提供两种方式实现线程,分别为继承 java.lang.Thread类与实现java.lang.Runnable接口。

1、继承Thread类

Thread类是java.lang包中的一个类,从这个类中实例化的对象代表线程,程序启动一个新线程需要建立Thread实例。常用的构造方法如下:

  1. public Thread():创建一个新线程
  2. public Thread(String threadName):创建一个名称为threadName的线程对象

完成线程真正功能的代码放在类的run()方法中,当一个类继承Thread类后,就可以在该类中覆盖run()方法,将实现该线程功能的代码写入run()方法中,然后同时调用Thread类中的start()方法执行该线程,也就是调用run()方法。

Thread对象需要一个任务来执行,任务是指线程在启动时执行的工作,该工作的功能代码被卸载run()方法中。run()方法的格式必须使用以下语法格式:

public void run(){
}

注意:如果start()方法调用了一个已启动的线程,系统将会抛出IllegaThreadStateException异常。

例:

public class ThreadTest extends Thread {

    private int count = 10;
    
    public void run(){
        
        while(){
           
            if(--count == 0){
                return ;
            }    
        }
    }
}

public static void main(){
    new ThreadTest().start();
}

在上述实例中,继承了Thread类,然后在类中覆盖了run()方法。通常在run()方法中使用无限虚幻的形式,使得线程一直运行下去,所以要指定一个跳出循环的条件。

在main方法中,使用线程执行需要调用Thread类中的start()方法,start()方法调用被覆盖的run()方法,如果不调用start()方法,线程永远都不会启动,在主方法没有调用start()方法之前,Thread对象只是一个实例,而不是一个真正的线程。

2、实现Runnable接口

实现Runnable接口的程序会创建一个Thread对象,并将Runnable对象与Thread对象相关联。Thread类中有以下两个构造方法:

  1. public Thread(Runnable target)
  2. public Thread(Runnable target,String name)

使用Runnable接口启动新的线程步骤如下:

  1. 建立Runnable对象
  2. 使用参数为Runnable对象的构造方法创建Thread实例
  3. 调用start()方法启动线程

使用Runnable接口创建线程时,程序首先需要编写一个实现Runnable接口的类,然后实例化该类的对象,这样就建立了Runnable对象,接下来使用相应的构造方法创建Thread实例,最后使用该实例调用Thread类中的start()方法启动线程。

注意:启动一个新的线程,不是直接调用Thread子类对象的run()方法,而是调用Thread子类的start()方法,Thread类的start()方法产生一个新的线程,该线程运行Thread子类的run()方法。

线程的生命周期

线程具有 出生状态、就绪状态、运行状态、等待状态、休眠状态、阻塞状态和死亡状态7中类型。

出生状态就是线程被创建时处于的状态,在用户使用该线程实例调用start()方法之前线程都处于出生状态;当用户调用start()方法之后,线程处于就绪状态(又被称为可执行状态);当线程得到系统资源后就进入运行状态。一旦线程进入可执行状态,他会在就绪与运行状态下转换,同时也有可能进入等待、休眠、阻塞或死亡状态。当处于运行状态下的线程调用Thread类中的wait()方法时,该线程便进入等待状态,进入等待状态的线程必须调用Thread类中的notify()方法才能被唤醒,而notifyAll()方法是将所有处于等待状态下的线程唤醒;当线程调用Thread类中的sleep()方法时,则会进入休眠状态。如果一个线程在运行状态下发出输入/输出请求,该线程将进入阻塞状态,在其等待输入/输出结束时线程进入就绪状态,对于阻塞线程来说,即使系统资源空闲,线程依然不能回到运行状态。当线程的run()方法执行完毕时,线程进入死亡状态。

虽然多线程看起来像同时执行,但事实上在同一时间点上只有一个线程被执行,只是线程之间切换较快,所以才会使人产生线程是同时进行的假象。在Windows操作系统中,系统会为每个线程分配一小段CPU时间片,一旦CPU时间片结束就会将当前线程换为下一个线程,即使该线程并没有结束。

使线程处于就绪状态的几种方法:

  1. 调用sleep()方法
  2. 调用wait()方法
  3. 等待输入/输出完成

当线程处于就绪状态后,可以用以下几种方法使线程在此进入运行状态:

  1. 线程调用notify()方法
  2. 线程调用notifyAll()方法
  3. 线程调用interrupt()方法
  4. 线程的休眠时间结束
  5. 输入/输出结束

线程的休眠

sleep()方法需要一个参数用于指定该线程休眠的时间,该时间以毫秒为单位。它通常实在run()方法内的循环中被使用。

try{
    Thread.sleep(2000); 
}catch(InterruptedException e){
    e.printStackTrace();
}

上述代码会使线程在2秒之内进入就绪状态。由于sleep()方法的执行有可能抛出InterruptedException异常,所以将sleep()方法的调用放在try-catch块中。虽然使用了sleep()方法的线程会在一段时间内醒来,但是并不保证它醒来后直接进入运行状态,只能保证其进入就绪状态。

线程的加入

如果当前某线程为多线程程序,假如存在一个线程A,现在需要插入线程B,并要求线程B先执行完,然后在执行线程A,此时可以使用Thread类中的join()方法来完成。

当某个线程使用join()方法加入到另一个线程时,另一个线程会等待该线程执行完毕后再继续执行。

线程的中断

以往有的时候会使用stop()方法停止线程,但当前版本的JDK早已废除了stop()方法,不建议在使用。现在提倡在run()方法中使用无限循环的形式,然后用一个Boolean型标记控制循环的停止。

如果线程是因为使用了sleep()或wait()方法进入了就绪状态,可以使用Thread类中interrupt()方法实现从离开run()方法,同时结束线程,但程序会抛出InterruptedException异常,在异常处理结束了while循环。项目中,经常在这里执行关闭数据库连接和关闭Socket链接等操作。

线程的礼让

Thread类中提供了一种礼让方法,使用yield()方法表示,它只是给当前正处于运行状态的线程一个提醒,告知它可以将资源礼让给其他线程,但这仅仅时一种暗示,没有任何一种机制保证当前线程会将资源礼让。

yield()方法使具有相同优先级的线程有进入可执行状态的机会,当当前线程放弃执行权时会再度回到就绪状态。对于支持多任务的操作系统来说,不需要调用yield()方法,因为操作系统会为线程自动分配CPU时间片来执行。

线程的优先级

每个线程都具有各自的优先级,线程的优先级可以表明在程序中该线程的重要性,如果有很多线程处于就绪状态,系统会根据优先级来决定首先使哪个线程进入运行状态。但这并不意味着低优先级的线程得不到运行,而是它运行的几率比较低,如垃圾回收线程的优先级就比较低。

Thread类中包含的成员变量代表了线程的某些优先级,如Thread.MIN_PRIORITY(常数1)、Thread.MAX_PRIORITY(常数10)、Thread.NORM_PRIORITY(常数5)。其中每个线程的优先级都在Thread.MIN_PRIORIT ~ Thread.MAX_PRIORITY之间,在默认情况下其优先级都是Thread.NORM_PRIORITY。每个新产生的线程都继承了浮现出的优先级。

线程的优先级可以使用setPriority()方法调整,如果使用该方法设置的优先级不在 1~10之内,将产生IllegalArgumentException异常。

线程同步

Java提供了线程同步的机制来防止资源访问的冲突。

1、同步块

在Java中提供了同步机制,可以有效地防止资源冲突,同步制使用synchronized关键字。

synchronized(Object){
}

通常将共享资源的操作放置在 synchronized 定义的区域内,这样当其他线程也获取到这个锁时,必须等待锁被释放时才能进入该区域。Object为任意一个对象,每个对象都存在一个标志位,并具有两个值,分别为0和1。一个线程运行到同步块时首先检查该对象的标志位,如果为0状态,表面此同步代码块中存在其他线程在运行。这时该线程处于就绪状态,直到处于同步块中的线程执行完同步块中的代码为止。这时该对象的标志位被设置位1,该线程才能执行同步块中的代码,并将对象的标志位设置位0,防止其他线程执行同步块中的代码。

2、同步方法

同步方法就是在方法前面修饰 synchronized 关键字的方法,其语句如下:

synchronized void f(){}

当某个对象调用了同步方法时,该对象上的其他同步方法必须等待该同步方法执行完毕后才能被执行。必须将每个能访问共享资源的方法修饰为synchronized,否则就会出错。

网络通信

InetAddress类

java.net包中的InetAddress类时与IP地址相关的类,利用该类可以获取IP地址、主机地址等信息。

InetAddress类常用方法
方法 返回值 说明
getByName(String host) InetAddress 获取与Host相对应的InetAddress对象
getHostAddress() String 获取InetAddress对象所包含的IP地址
getHostName() String 获取此IP地址的主机名
getLocalHost() InetAddress 返回本地主机的InetAddress对象

注意:InetAddress类的方法会抛出UnknownHostException异常,所以必须进行异常处理。这个异常在本机不存在或网络连接错误时发生。

ServerSocket类

java.net包中的ServerSocket类用于表示服务器套接数字,其主要功能时等待来自网络上的”请求“,它可通过指定的端口来等待连接的套接字。服务器套接字依次可以与一个套接字连接,如果多台客户机同时提出连接请求,服务器套接字会将请求连接的客户机存入列队中,然后从中取出一个套接字与服务器新建的套接字连接起来。若请求连接数大于最大容纳数,则多出来的连接请求被拒绝。列队默认大小是50。

ServerSocket类的构造方法都抛出IOException异常,分别由以下几种形式:

  1. ServerSocket():创建非绑定服务器套接字
  2. ServerSocket(int port,intbacklog):利用指定的backlog创建服务器套接字并将其绑定到指定本地端口号
  3. ServerSocket(int port,intbacklog,InetAddress bindAddress):使用指定的端口、侦听backlog要绑定到的本地IP地址创建服务器。这种情况适用于计算机上由多块网卡和多个IP地址的情况,用于可以明确规定ServerSocket在哪块网卡或IP地址上等待客户的连接请求。
ServerSocket类常用方法
方法 返回值 说明
accept() Socket 等待客户机的连接。若连接,则创建一套接字
isBound() boolean 判断ServerSocket的绑定状态
getInetAddress() InetAddress 返回此服务器套接字的本地地址
isClosed() boolean 返回服务器套接字的关闭状态
close() void 关闭服务器套接字
bind(SocketAddress endpoint) void 将ServerSocket绑定到特定地址(IP地址和端口号)
getInetAddress() int 返回服务器套接字等待的端口号

调用ServerSocket类的accept()方法会返回一个和客户端Socket对象相连接的Socket对象,服务器端的Socket对象使用getOutputStream()方法获得的输出流将指向客户端Socket对象使用getInputStream()方法获得的那个输入流;同样,服务器端的Socket对象使用getInputStream()方法获得的输入流将指向客户端Socket对象使用getOutputStream()方法获得的那个输出流。也就是说,当服务器项输出流写入信息时,客户端通过相应的输入流就能读取,反之亦然。

注意:accept()方法会阻塞线程的继续执行,直到接收到客户的呼叫。如果没有客户呼叫服务器,那么System.out.println("连接中")语句将不会执行。语句如果没有客户端请求,accept()方法没有发生阻塞,肯定是程序出现了问题。通常是使用看一个还在被其他程序占用的端口号,ServerSocket绑定没有成功。例:

yu = server.accept();
System.out.println("连接中");

 

未完待续----

你可能感兴趣的:(Java)