Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 

README.md

2021年最新整理,名企校招(含技术细节)Java岗位。持续更新中...


Java基础

Java基础知识

Java语言的特点

  • 1.面向对象(封装,继承,多态);
  • 2.平台无关性( Java 虚拟机实现平台无关性,一次编译,到处运行);
  • 3.简单易学(与C语言的面向过程相比,Java的面向对象更接近人的语言习惯);
  • 4.安全性,可靠性(Java中没有指针,程序员无法直接操作内存,而是把操作权限交给Java虚拟机,使程序不容易出现不容易出现内存泄漏和内存溢出问题。);
  • 5.支持多线程( C++ 语言没有内置的多线程机制,因此必须调用操作系统的多线程功能来进行多线程程序设计,而 java的lang包提供一个Thread类本身就支持多线程);
  • 6.编译与解释并存(Java编译生成字节码文件,交给Java虚拟机解释);

面向对象与面向过程对比

  • 1.面向过程
  • 优点: 性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗资源;比如单片机、嵌入式开发、Linux/Unix等一般采用面向过程开发,性能是最重要的因素。
  • 缺点: 没有面向对象易维护、易复用、易扩展。
  • 2.面向对象
  • 优点: 易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护。
  • 缺点: 性能比面向过程低。

Java平台的三个版本J2EE、J2SE、J2ME

  • JavaSE:即Java标准版,主要用于开发和部署桌面、例如,Java应用程序开发平台Eclipse(常说的C\S架构)。
  • JavaEE:即Java企业版,主要针对企业应用的开发。例如,电商网站(常说的B\S架构)。
  • JavaME:即Java微型版,主要针对移动设备和嵌入式设备。例如,手机、PDA、电视机顶盒等等。
  • 注:从JDK 5.0开始 J2EE 改名为 java EE,J2SE 改名为 java SE,J2ME 改名成 java ME。

JDK、JRE、JVM之间的区别于关系

  • 1.三者之间的区别
  • JDK:(Java Development Kit)即java的开发与运行环境,他除了包含完整的JRE之外,还包含了供开发者使用的工具包。

  • JRE:(Java Runtime Environment)即Java运行环境,非开发者只需要安装 JRE来运行程序, 它包含java运行的所需的类库+JVM(java虚拟机)。

  • JVM: (Java Virtual Machine) 即Java虚拟机, 当我们运行一个程序时,JVM 负责将字节码转换为特定机器代码,JVM 提供了内存管理/垃圾回收和安全机制等。这种独立于硬件和操作系统,正是 java 程序可以一次编写多处执行的原因。

  • 2.三者之间的关系
  • 作为程序员,就必须安装JDK,因为其中包含Java开发工具包,同时也包含了JRE。

  • 作为使用者,运行已经开发好的Java程序,只需要安装JRE。

  • JVM和JRE的关系:JRE包含了JVM,JVM是运行Java程序的核心虚拟机,同时也包含了Java程序所需的环境支持

  • 总结:JDK>JRE>JVM

  • JDK (Java开发工具包)

  • JRE (Java运行环境)

  • JVM (Java虚拟机)

Java环境变量

  • 1.环境变量的意义
  • 让java bin目录下的工具,可以在任意目录下运行,原理是将该工具所在目录告诉了系统,当使用该工具时,由系统帮我们去找指定的目录。
  • 2.JAVA_HOME
  • 它指向jdk的安装目录,引用%JAVA_HOME%即可,避免每次引用都输入很长的路径串,方便第三方软件引用约定好的JAVA_HOME变量,保证程序正常运行。
  • 3.Path环境变量
  • 设置Path环境变量之后就可以在任何目录下执行javac/java等工具命令了。 系统默认先去当前路径下找要执行的程序,如果没有,再去path中设置的路径下找。
  • 4.ClassPath
  • 如果指定了classpath,那么会在指定的目录下查找要运行的类文件(JDK1.5后不需要配置)

javac命令和java命令

  • java运行分两部分:一个是编译,一个是运行。
  • javac:负责的是编译的部分,当执行javac时,会启动java的编译器程序。对指定扩展名的.java文件进行编译。编译后生成class文件。
  • java:负责运行的部分.会启动jvm虚拟机,加载运行时所需的类库,并对class文件进行执行.

什么是字节码,采用字节码的好处是什么。

  • 首先我们来谈谈Java文件类型,一共有两种:
    1. 扩展名为Java,Java的源文件,编译之前的纯文本文件,用来储存Java源代码。
    1. 扩展名为class,Java 的类文件,编译之后的二进制文件,存储的是字节码
  • 也就是说编译后的.class文件存储就是字节码*。
  • 采用字节码的最大好处: 可以实现一次编译到处运行,也就是java的与平台无关性,它依靠不同平台的Java虚拟机将编译后的字节码解释成具体平台上的机器指令执行。

importjava和javax有什么区别

  • 刚开始的时候 JavaAPI 所必需的包是 java 开头的包,javax 当时只是扩展 API 包来说使用。然而随着时间的推移,javax 逐渐的扩展成为 Java API 的组成部分。但是,将扩展从 javax 包移动到 java 包将是* 太麻烦了,最终会破坏一堆现有的代码。因此,最终决定 javax 包将成为标准API的一部分。
  • 所以,实际上java和javax没有区别。这都是一个名字。

Java和C++的区别

  • 都是面向对象的语言,都支持封装、继承和多态
  • Java 不提供指针来直接访问内存,程序内存更加安全
  • Java 的类是单继承的,C++ 支持多重继承;虽然 Java 的类不可以多继承,但是接口可以多继承。
  • Java 有自动内存管理机制,不需要程序员手动释放无用内存

Java数据类型

基本数据类型

  • 整型、浮点型、字符型、布尔型

引用类型

  • 类、接口类型、数组类型、枚举类型、注解类型

区别

  • 基本数据类型 在被创建时,在栈上给其划分一块内存,将数值直接存储在栈上。
  • 引用数据类型 在被创建时,首先要在栈上给其引用(句柄)分配一块内存,而对象的具体信息都存储在堆内存上,然后由栈上面的引用指向堆中对象的地址

Java访问修饰符

字符型常量和字符串常量的区别

面向对象编程三大特性:封装、继承、多态

封装

  • 封装把一个对象的属性私有化,同时提供一些可以被外界访问的属性的方法,如果属性不想被外界访问,我们大可不必提供方法给外界访问。

继承

  • 继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。通过使用继承我们能够非常方便地复用以前的代码 同时继承也为实* * 现多态做了铺垫。
    • 关于继承
    • 子类拥有父类非 private 的属性和方法。
    • 子类可以拥有自己属性和方法,即子类可以对父类进行扩展。

多态

  • 所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到* 底是哪个类中实现的方法,必须在由程序运行期间才能决定。
    • 体现: 父类引用或者接口的引用指向了自己的子类对象
  • 多态实现三种方式:
    • 接口实现
    • 继承父类重写方法
    • 同一类中进行方法重载
  • 多态好处:
    • 允许不同类对象对同一消息做出响应,即同一消息可以根据发送对象的不同而采用多种不同的行为方式(发送消息就是函数调用)。主要有以下优点:
    • 可替换性:多态对已存在代码具有可替换性
    • 可扩充性:增加新的子类不影响已经存在的类结构
    • 接口性:多态是超类通过方法签名,向子类提供一个公共接口,由子类来完善或者重写它来实现的。

接口和抽象类的区别是什么

接口的意义

  • 接口的意义用三个词就可以概括:规范,扩展,回调。

抽象类的意义

  • 抽象类的意义可以用三句话来概括:
  • 为其他子类提供一个公共的类型
  • 封装子类中重复定义的内容
  • 定义抽象方法,子类虽然有不同的实现,但是定义时一致的

重载和重写的区别

  • 重载: 发生在同一个类中,方法名必须相同,参数类型不同、个数不同、顺序不同,方法返回值和访问修饰符可以不同,发生在编译时。
  • 重写: 发生在父子类中,方法名、参数列表必须相同,返回值范围小于等于父类,抛出的异常范围小于等于父类,访问修饰符范围大于等于父类;如果父类方法访问修饰符为 private 则子类就不能重写该方法。

构造方法

构造方法特性

  • 名字与类名相同;
  • 没有返回值,但不能用void声明构造函数;
  • 生成类的对象时自动执行,无需调用。

  • 在讲继承的时候我们就知道父类的私有属性和构造方法并不能被继承,所以 Constructor 也就不能被 override(重写),但是可以 overload(重载),所以你可以看到一个类中有多个构造函数的情况。

成员变量与局部变量的区别有那些

  • 从语法形式上,看成员变量是属于类的,而局部变量是在方法中定义的变量或是方法的参数;成员变量可以被 public,private,static 等修饰符所修饰,而局部变量不能被访问控制修饰符及 static 所修饰;但是,* 成员变量和局部变量都能被 final 所修饰;
  • 从变量在内存中的存储方式来看,成员变量是对象的一部分,而对象存在于堆内存,局部变量存在于栈内存。
  • 从变量在内存中的生存时间上看,成员变量是对象的一部分,它随着对象的创建而存在,而局部变量随着方法的调用而自动消失。
  • 成员变量如果没有被赋初值,则会自动以类型的默认值而赋值(一种情况例外被 final 修饰的成员变量也必须显示地赋值);而局部变量则不会自动赋值。

java对象

java创建对象的方式

  • 采用new(对象实例在堆内存中),对象引用指向对象实例(对象引用存放在栈内存中)
  • 通过反射
  • 采用clone
  • 通过序列化机制
  • 前2者都需要显式地调用构造方法。造成耦合性最高的恰好是第一种,因此你发现无论什么框架,只要涉及到解耦必先减少new的使用。

Object中公共方法

  • equals()
  • clone()
  • getClass()
  • notify(),notifyAll(),wait()
  • toString
  • finalize()

java引用

java中四种引用

  • 强引用,软引用,弱引用,虚引用。不同的引用类型主要体现在GC上:

强引用(StrongReference)

  • 如果一个对象具有强引用,它就不会被垃圾回收器回收。即使当前内存空间不足,JVM也不会回收它,而是抛出 OutOfMemoryError 错误,使程序异常终止。如果想中断强引用和某个对象之间的关联,可以显式地将引用赋值为null,这样一来的话,JVM在合适的时间就会回收该对象。
  • 如obj.equels(new Object());
  • 而这样 obj对象对后面new Object的一个强引用,只有当obj这个引用被释放之后,对象才会被释放

软引用(SoftReference)

  • 在使用软引用时,如果内存的空间足够,软引用就能继续被使用,而不会被垃圾回收器回收,只有在内存不足时,软引用才会被垃圾回收器回收。

弱引用(WeakReference)

  • 具有弱引用的对象拥有的生命周期更短暂。因为当 JVM 进行垃圾回收,一旦发现弱引用对象,无论当前内存空间是否充足,都会将弱引用回收。不过由于垃圾回收器是一个优先级较低的线程,所以并不一定能迅速发现弱引用对象。

虚引用(PhantomReference)

  • 顾名思义,就是形同虚设,如果一个对象仅持有虚引用,那么它相当于没有引用,在任何时候都可能被垃圾回收器回收。

WeakReference与SoftReference的区别

  • WeakReference 与 SoftReference 都有利于提高 GC 和 内存的效率,但是 WeakReference ,一旦失去最后一个强引用,就会被 GC 回收,而软引用虽然不能阻止被回收,但是可以延迟到 JVM 内存不足的时候。

为什么要有不同的引用类型

  • java不像C语言,我们可以控制内存的申请和释放,在Java中有时候我们需要适当的控制对象被回收的时机,因此就诞生了不同的引用类型,可以说不同的引用类型实则是对GC回收时机不可控的妥协。有以下几个使用场景可以充分的说明:
  • 利用软引用和弱引用解决OOM问题:用一个HashMap来保存图片的路径和相应图片对象关联的软引用之间的映射关系,在内存不足时,JVM会自动回收这些缓存图片对象所占用的空间,从而有效地避免了OOM的问题.
  • 通过软引用实现Java对象的高速缓存:比如我们创建了一Person的类,如果每次需要查询一个人的信息,哪怕是几秒中之前刚刚查询过的,都要重新构建一个实例,这将引起大量Person对象的消耗,并且由于这些对象的生命周期相对较短,会引起多次GC影响性能。此时,通过软引用和 HashMap 的结合可以构建高速缓存,提供性能。

String和StringBuffer、StringBuilder

可变性

  • 简单的来说:String 类中使用 final 关键字字符数组保存字符串,private final char value[],所以 String 对象是不可变的。而StringBuilder 与 StringBuffer 都继承自 AbstractStringBuilder * 类,在 AbstractStringBuilder 中也是使用字符数组保存字符串char[]value 但是没有用 final 关键字修饰,所以这两种对象都是可变的

线程安全性

  • String 中的对象是不可变的,也就可以理解为常量,线程安全。AbstractStringBuilder 是 StringBuilder 与 StringBuffer 的公共父类,定义了一些字符串的基本操作,如 expandCapacity、append、
  • insert、indexOf 等公共方法。StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder 并没有对方法进行加同步锁,所以是非线程安全的。   
性能
  • 每次对 String 类型进行改变的时候,都会生成一个新的 String 对象,然后将指针指向新的 String 对象。StringBuffer 每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象并改变对象引用。相* 同情况下使用 StirngBuilder 相比使用 StringBuffer 仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险。
  • 对于三者使用的总结:
  • 操作少量的数据 = String
  • 单线程操作字符串缓冲区下操作大量数据 = StringBuilder
  • 多线程操作字符串缓冲区下操作大量数据 = StringBuffer

== 与 equals()

==

  • 它的作用是判断两个对象的地址是不是相等。即,判断两个对象是不是同一个对象。(基本数据类型“= =”比较的是值,引用数据类型 = = 比较的是内存地址).

equals()

  • 它的作用也是判断两个对象是否相等。但它一般有两种使用情况:
  • 情况1:类没有覆盖 equals() 方法。则通过 equals() 比较该类的两个对象时,等价于通过“==”比较这两个对象。
  • 情况2:类覆盖了 equals() 方法。一般,我们都覆盖 equals() 方法来两个对象的内容相等;若它们的内容相等,则返回 true (即,认为这两个对象相等)。
  • 说明:
  • String 中的 equals 方法是被重写过的,因为 object 的 equals 方法是比较的对象的内存地址,而 String 的 equals 方法比较的是对象的值。
  • 当创建 String 类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要创建的值相同的对象,如果有就把它赋给当前引用。如果没有就在常量池中重新创建一个 String 对象。

hashCode() 与 equals()

hashCode()

  • hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。hashCode() 定义在JDK的Object.java中,这就意味着Java中的任何类都包含有hashCode() 函数。
  • 散列表存储的是键值对(key-value),它的特点是:能根据“键”快速的检索出对应的“值”。这其中就利用到了散列码!(可以快速找到所需要的对象)。

为什么要有 hashCode()

  • 我们以“HashSet 如何检查重复”为例子来说明为什么要有 hashCode:
  • 当你把对象加入 HashSet 时,HashSet 会先计算对象的 hashcode 值来判断对象加入的位置,同时也会与其他已经加入的对象的 hashcode 值作比较,如果没有相符的hashcode,HashSet会假设对象没有重复出现。但是如果发现有相同 hashcode 值的对象,这时会调用 equals()方法来检查 hashcode 相等的对象是否真的相同。如果两者相同,HashSet 就不会让其加入操作成功。如果不同的话,就会重新散列到其他位置。(摘自我的Java启蒙书《Head fist java》第二版)。这样我们就大大减少了 equals 的次数,相应就大大提高了执行速度。

hashCode()与equals()的相关规定

  • 如果两个对象相等,则hashcode一定也是相同的。
  • 两个对象相等,对两个对象分别调用equals方法都返回true。
  • 两个对象有相同的hashcode值,它们也不一定是相等的。
  • 因此,equals 方法被覆盖过,则 hashCode 方法也必须被覆盖,hashCode() 的默认行为是对堆上的对象产生独特值。如果没有重写 hashCode(),则该 class 的两个对象无论如何都不会相等。

final, finally, finalize的区别

final

  • 修饰类 表示该类不能被继承
  • 修饰方法 表示该方法不能被重写
  • 修饰基本类型变量 表示该变量只能被赋值一次,如果修饰引用,那么表示引用不可变,引用指向的内容可变。
  • 被final修饰的方法,JVM会尝试将其内联,以提高运行效率
  • 被final修饰的常量,在编译阶段会存入常量池中。

finally

  • finally 是用于异常处理的场面,无论是否有异常抛出,都会执行

finalize

  • finalize是Object的方法,所有类都继承了该方法。 当一个对象满足垃圾回收的条件,并且被回收的时候,其finalize()方法就会被调用

Java异常

Java异常处理

  • java.lang包中的 Throwable类。Throwable: 有两个重要的子类:Exception(异常) 和 Error(错误) ,二者都是 Java 异常处理的重要子类,各自都包含大量子类。

Error(错误)

  • 程序无法处理的错误,表示运行应用程序中较严重问题。大多数错误与代码编写者执行的操作无关,而表示代码运行时 JVM(Java 虚拟机)出现的问题。例如,Java虚拟机运行错误(Virtual MachineError),当 JVM 不再有继续执行操作所需的内存资源时,将出现 OutOfMemoryError。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止。

Exception(异常)

  • 程序本身可以处理的异常。 Exception 类有一个重要的子类 RuntimeException。RuntimeException 异常由Java虚拟机抛出。NullPointerException(要访问的变量没有引用任何对象* 时,抛出该异常)、ArithmeticException(算术运算异常,一个整数除以0时,抛出该异常)和 ArrayIndexOutOfBoundsException (下标越界异常)。

Throwable类常用方法

    1. public string getMessage():返回异常发生时的详细信息
    1. public string toString():返回异常发生时的简要描述
    1. public void printStackTrace():在控制台上打印Throwable对象封装的异常信息

异常处理总结

  • try 块: 用于捕获异常。其后可接零个或多个catch块,如果没有catch块,则必须跟一个finally块。
  • catch 块:用于处理try捕获到的异常。
  • finally 块: 无论是否捕获或处理异常,finally块里的语句都会被执行。当在try块或catch块中遇到return语句时,finally语句块将在方法返回之前被执行

finally块不会被执行的情况:

  • 在finally语句块中发生了异常。
  • 在前面的代码中用了System.exit()退出程序。
  • 程序所在的线程死亡。
  • 关闭CPU。

RuntimeException与Error

runtime exception
  • NullPointerException (空指针异常)
  • ArrayIndexOutOfBoundsException(数组下标越界)
  • IllegalArgumentException (参数错误)
  • ArithmeticException 算术异常,比如除数为零
  • ClassCastException 类型转换异常
Error
  • OutOfMemoryError (堆内存溢出)
  • StackOverflowError(栈内存溢出)

Java反射

java反射介绍

编译期和运行期,编译期就是编译器帮你把源代码翻译成机器能识别的代码,比如编译器把java代码编译成jvm识别的字节码文件,而运行期指的是将可执行文件交给操作系统去执行,JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制

java反射(Reflection)的底层实现原理

Object 类,是所有Java 类的继承根源,其内声明了数个应该在所有Java 类中被改写的方法:其中getClass()返回一个Class 对象

创建反射实例

直接通过类名点.class获取 通过Object类的getClass方法来获取 通过Object类的getClass方法来获取 通过全类名获取用的比较多推荐使用 例如:Class.forName(“com.mysql.jdbc.Driver”);

反射中,Class.forName和classloader的区别

java中class.forName()和classLoader都可用来对类进行加载。 class.forName()前者除了将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static块。 而classLoader只干一件事情,就是将.class文件加载到jvm中,不会执行static中的内容,只有在newInstance才会去执行static块。

序列化与反序列化

序列化

简单说就是将内存中的对象保存下来,并且可以把保存的对象状态再读出来。 实现java序列化的手段是让该类实现接口 Serializable,这个接口是一个标识性接口,没有任何方法,仅仅用于表示该类可以序列化。

什么情况下需要序列化

  • 当你想把的内存中的对象保存到一个文件中或者数据库中时候;
  • 当你想用序列化在网络上传送对象的时候;
  • 当你想通过RMI传输对象的时候;

序列化注意事项

a) 当一个父类实现序列化,子类自动实现序列化,不需要显式实现Serializable接口; 把一个对象完全转成字节序列,方便传输。 就像你寄一箱饼干,因为体积太大,就全压成粉末紧紧地一包寄出去,这就是序列化的作用。 只不过JAVA的序列化是可以完全还原的。

Java序列化如果有些字段不想进行序列化,如何处理?

对于不想进行序列化的变量,使用transient关键字修饰。 transient关键字的作用是:阻止实例中那些用此关键字修饰的的变量序列化;当对象被反序列化时,被transient修饰的变量值不会被持久化和恢复。transient只能修饰变量,不能修饰类和方法。

java拷贝

浅拷贝与深拷贝

  1. 浅拷贝: 只复制一个对象,对象内部存在的指向其他对象数组或者引用则不复制
  2. 深拷贝: 深拷贝:对象,对象内部的引用均复制

拷贝的几种方法

  1. System.arraycopy(浅拷贝)
  • public static native void arraycopy(Object src, int srcPos,Object dest, int destPos,int length);
  • 通过源代码我们可以看到,关键字native说明它不是用java语言写的,而是调用其他语言的代码。
  1. Arrays.copyOf(浅拷贝) 实际上它调用的就是System.arraycopy.
  2. Object.clone clone()比较特殊,对于对象而言,它是深拷贝,但是对于数组而言,它是浅拷贝。

java流

字节流

Java中所有的流都是基于字节流,所以最基本的流是 字节流

输入输出字节流

InputStream OutputStream

字符流

在字节流的基础上,封装了字符流

Reader Writer

缓存流

进一步,又封装了缓存流

BufferedReader PrintWriter

数据流

DataInputStream DataOutputStream

对象流

ObjectInputStream ObjectOutputStream

获取键盘输入

通过 Scanner

Scanner input = new Scanner(System.in);
String s  = input.nextLine();
input.close();

通过 Scanner

BufferedReader input = new BufferedReader(new InputStreamReader(System.in)); 
String s = input.readLine(); 

JavaWeb模块

InetAddress

  • 此类表示互联网协议 (IP) 地址。

常用方法

  • 此类表示互联网协议 (IP) 地址。
byte[] getAddress()				返回此 InetAddress 对象的原始 IP 地址static InetAddress getByName(String host)	在给定主机名的情况下确定主机的 IP 地址String getHostAddress()				返回 IP 地址字符串以文本表现形式)。
String getHostName()				获取此 IP 地址的主机名static InetAddress getLocalHost()		返回本地主机

案例

案例1
import java.net.InetAddress;
import java.net.UnknownHostException;

public class TestIP {
    public static void main(String[] args) throws UnknownHostException {
        //InetAdress类表示IP地址

        //获取本机IP
        InetAddress ip = InetAddress.getLocalHost();// ADMINISTRATOR/192.xxx.xxx.xxx
        System.out.println(ip);
        //获得主机名
        System.out.println(ip.getHostName());// ADMINISTRATOR
        //获得IP地址
        System.out.println(ip.getHostAddress());// 192.xxx.xxx.xxx
        //getLocalHost=getHostName+getHostAddress
        System.out.println(InetAddress.getByName("localhost"));
    }
}
案例2
import java.net.InetAddress;
import java.net.UnknownHostException;

public class TestIP2 {
    public static void main(String[] args) throws UnknownHostException {
        InetAddress inetAddress = InetAddress.getByName("www.baidu.com");
        // 获取此 IP 地址的主机名。
        System.out.println(inetAddress.getHostName());
        //返回 IP 地址字符串(以文本表现形式)。
        System.out.println(inetAddress.getHostAddress());
    }
}

InetSocketAddress

构造方法

InetSocketAddress(InetAddress addr, int port)			根据 IP 地址和端口号创建套接字地址InetSocketAddress(int port)					创建套接字地址其中 IP 地址为通配符地址端口号为指定值InetSocketAddress(String hostname, int port)			根据主机名和端口号创建套接字地址

常用方法

InetAddress getAddress()					获取 InetAddressString getHostName()						获取 hostname。
int getPort()							获取端口号
案例
import java.net.InetAddress;
import java.net.InetSocketAddress;

public class TestPort {
    public static void main(String[] args) {
        InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1",8082);
        System.out.println(inetSocketAddress);
        //返回主机名
        System.out.println(inetSocketAddress.getHostName());
        //获得InetSocketAddress的端口
        System.out.println(inetSocketAddress.getPort());
        //返回一个InetAddress对象(IP对象)
        InetAddress address = inetSocketAddress.getAddress();
        System.out.println(address);
    }
}

URL

  • 类 URL 代表一个统一资源定位符,它是指向互联网“资源”的指针。

Socket和 ServerSocket

Socket

  • 此类实现客户端套接字,套接字指的是两台设备之间通讯的端点。 (1)构造方法摘要
public Socket(String host, int port) :创建套接字对象并将其连接到指定主机上的指定端口号如果指定的host是null则相当于指定地址为回送地址回送地址(127.x.x.x) 是本机回送地址Loopback Address),主要用于网络软件测试以及本地机进程间通信无论什么程序一旦使用回送地址发送数据立即返回不进行任何网络传输

(2)常用方法摘要

public InputStream getInputStream() : 返回此套接字的输入流如果此Scoket具有相关联的通道则生成的InputStream 的所有操作也关联该通道关闭生成的InputStream也将关闭相关的Socketpublic OutputStream getOutputStream() : 返回此套接字的输出流如果此Scoket具有相关联的通道则生成的OutputStream 的所有操作也关联该通道关闭生成的OutputStream也将关闭相关的Socketpublic void close() :关闭此套接字一旦一个socket被关闭它不可再使用关闭此socket也将关闭相关的InputStream和OutputStreampublic void shutdownOutput() : 禁用此套接字的输出流任何先前写出的数据将被发送随后终止输出流

InetSocketAddress类

  • 此类实现 IP 套接字地址(IP 地址 + 端口号)。 (1)构造方法摘要
InetSocketAddress(InetAddress addr, int port)				根据 IP 地址和端口号创建套接字地址InetSocketAddress(int port)						创建套接字地址其中 IP 地址为通配符地址端口号为指定值InetSocketAddress(String hostname, int port)				根据主机名和端口号创建套接字地址

(2)常用方法摘要

InetAddress getAddress()						获取 InetAddressString getHostName()							获取 hostname。		
int getPort()								获取端口号

案例

import java.net.InetAddress;
import java.net.InetSocketAddress;

public class TestPort {
    public static void main(String[] args) {
        InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1",8082);
        System.out.println(inetSocketAddress);
        //返回主机名
        System.out.println(inetSocketAddress.getHostName());
        //获得InetSocketAddress的端口
        System.out.println(inetSocketAddress.getPort());
        //返回一个InetAddress对象(IP对象)
        InetAddress address = inetSocketAddress.getAddress();
        System.out.println(address);
    }
}

ServerSocket

  • 此类实现了服务器套接字,该对象等待通过网络的请求。 (1)构造方法摘要
public ServerSocket(int port) :使用该构造方法在创建ServerSocket对象时就可以将其绑定到一个指定的端口号上参数port就是端口号

(2)常用方法摘要

public Socket accept() :侦听并接受连接返回一个新的Socket对象用于和客户端实现通信该方法会一直阻塞直到建立连接

Socket和ServerSocket案例

Socket

import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;

public class TCPClient {
    public static void main(String[] args){
        Socket socket = null;
        OutputStream os = null;
        try {
            //1、创建Socket对象,它的第一个参数需要的是服务端的IP,第二个参数是服务端的端口
            InetAddress inet = InetAddress.getByName("127.0.0.1");
            socket = new Socket(inet,8090);
            //2、获取一个输出流,用于写出要发送的数据
            os = socket.getOutputStream();
            //3、写出数据
            os.write("你好,我是客户端!".getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {//4、释放资源
            if(socket!=null){
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(os!=null){
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

ServerSocket

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class TCPServer {
    public static void main(String[] args) {
        ServerSocket serverSocket = null;
        Socket socket = null;
        InputStream is = null;
        ByteArrayOutputStream baos = null;
        try {
            //1、创建服务端的ServerSocket,指明自己的端口号
            serverSocket = new ServerSocket(8090);
            //2、调用accept接收到来自于客户端的socket
            socket = serverSocket.accept();//阻塞式监听,会一直等待客户端的接入
            //3、获取socket的输入流
            is = socket.getInputStream();

//        不建议这样写:因为如果我们发送的数据有汉字,用String的方式输出可能会截取汉字,产生乱码
//        int len=0;
//        byte[] buffer = new byte[1024];
//        while ((len=is.read(buffer))!=-1){
//            String str = new String(buffer, 0, len);
//            System.out.println(str);
//        }
            
            //4、读取输入流中的数据
            //ByteArrayOutputStream的好处是它可以根据数据的大小自动扩充
            baos = new ByteArrayOutputStream();
            int len=0;
            byte[] buffer = new byte[1024];
            while ((len=is.read(buffer))!=-1){
                baos.write(buffer,0,len);
            }
            System.out.println("收到了来自于客户端"+socket.getInetAddress().getHostName()
                    +"的消息:"+baos.toString());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {//5、关闭资源
            if(serverSocket!=null){
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(socket!=null){
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(is!=null){
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(baos!=null){
                try {
                    baos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}