代码审计——基础(JAVASE)
JAVASE
基本语法
关键字
定义:被Java语言赋予了特殊含义,用作专门用途的字符串
特点:关键字中所有字母都为小写
补充:java语言是强类型编程语言,严格区分变量的类型,弱类型编程语言,比如js
强类型:指的是定义数据类型变量类型需要强制的规定(程序员手动声明)类型的语言,例如java
弱类型:指的是定义数据类型(变量类型)无需规定类型的语言,例如js
用户定义数据类型的关键字 | ||||
---|---|---|---|---|
class | interface | enum | byte | short |
int | long | float | double | char |
boolean | void | string(引用类型) | ||
用于定义数据类型值的关键字 | ||||
true | false | null | ||
用于定义流程控制的关键字 | ||||
if | else | switch | case | default |
while | do | for | break | continue |
return |
用于定义访问权限修饰符的关键字(安全) | ||||
---|---|---|---|---|
private | protected | default | public | |
用户定义类,函数,变量修饰符的关键字 | ||||
abstract | final | static | synchronized | |
用于定义类与类之间关系的关键字 | ||||
extends | implements | |||
用于定义建立实例及引用实例,判断实例的关键字 | ||||
new | this | super | instanceof | |
用于异常处理的关键字 | ||||
try | catch | finally | throw | throws |
用于包的关键字 | ||||
package | import | |||
其它修饰符关键字 | ||||
native | strictfp | transient | volatile | assert |
变量
- 变量概念
- 内存中的一个存储区域
- 该区域有自己的变量名和数据类型
- JAVA中每个变量先声明,后使用
- 该区域的数据可以在同一类型范围内不断变化
- 定义变量的格式:数据类型 变量名=初始化值
- 变量是通过使用变量名来访问这块区域的
- Java是强类型语言,每个变量必须先声明类型后使用
- 使用变量注意:变量作用域:一对{ }之间有效
类型 | 占用存储空间 | 表数范围 |
---|---|---|
byte | 1字节 | -128~127 |
short | 2字节 | -2的15次方~2的15次方-1 |
int | 4字节 | -2的31次方~2的31次方-1 |
long | 8字节 | -2的63次方~2的63次方-1 |
float | 4字节 | -3.403E38~3.403E38 |
double | 8字节 | -1.798E308~1.798E308 |
作业1
作业
(1)简单的介绍了java语言历史,优势、发展
(2)特性:面向对象、跨平台、封装、继承、多态、抽象、扩展性、健壮性、垃圾回收机制、虚拟机
(3)变量、注释、关键字、数据类型、选择判断语句、循环语句、数组、break、continue、运算符
作业完成
第一题:简单的介绍了java语言历史,优势、发展
一、历史
Java语言最初是由美国Sun Microsystems公司(后被Oracle公司收购)在1995年推出的一门计算机高级编程语言。早期,这门语言的名字并不叫Java,而是称为Oak(橡树)。然而,由于商标注册的问题,Oak这个名称已经被其他公司注册,所以后来改名为Java。Java的联合创始人中,詹姆斯·高斯林(James Gosling)被普遍认为是Java的创始人,并被称为“Java之父”。
二、优势
- 纯面向对象:Java是一种纯面向对象的语言,这使得它更容易理解和使用。
- 平台无关性:Java是解释性语言,编译器会把Java代码变成“中间字节码”,然后在Java虚拟机(JVM)上解释执行。由于中间代码与平台无关,Java语言具有很好的跨平台特性,即“一次编写,到处运行”。
- 丰富的内置类库:Java提供了许多内置类库,这些类库可以大大简化开发人员的程序设计工作。例如,Java提供了对多线程、网络通信、垃圾回收等的支持,使得开发人员可以更加专注于业务逻辑的实现。
- Web应用开发支持:Java提供了对Web应用开发的支持,如Applet、Servlet、JSP等。这使得Java成为Web应用程序开发的首选语言之一。
- 安全性与健壮性:Java具有较好的安全性和健壮性。它提供了防止恶意代码攻击的安全机制(如数组边界检测和Bytecode校验等),以及强类型机制、垃圾回收器、异常处理和安全检查机制等,使得用Java语言编写的程序具有很好的健壮性。
三、发展
自Java诞生以来,它经历了不断的发展和壮大。Sun Microsystems以及其他公司和开发者对Java进行了不断的开发和扩展,使其应用场景越来越广泛。Java目前已成为企业级互联网应用程序开发的主流语言之一,并且还在大数据、游戏开发等领域得到了广泛应用。同时,Java也经历了多个版本的更新和迭代,如JDK 1.5(后更名为JDK 5.0)、JDK 7.0、JDK 8.0等,这些版本都带来了许多新的特性和改进。
总之,Java语言凭借其独特的历史背景、丰富的优势以及不断的发展壮大,已成为当今最受欢迎的编程语言之一。
第二题:特性:面向对象、跨平台、封装、继承、多态、抽象、扩展性、健壮性、垃圾回收机制、虚拟机
- 面向对象(Object-Oriented)
- Java是一种纯面向对象的语言,这意味着它使用类和对象来组织代码和数据。面向对象编程(OOP)的概念包括封装、继承和多态。
- 跨平台(Platform Independent)
- Java的“一次编写,到处运行”(Write Once, Run Anywhere, WORA)的特性是由Java虚拟机(JVM)实现的。JVM能够在不同的操作系统上运行,只要安装了相应的JVM版本,Java程序就可以在任何支持JVM的平台上运行。
- 封装(Encapsulation)
- 封装是一种将数据(属性)和行为(方法)捆绑在一起作为一个单独的对象的方法。在Java中,这通常是通过类来实现的,类的成员变量(属性)和成员方法(行为)都被封装在类内部。
- 继承(Inheritance)
- 继承是面向对象编程的一个重要概念,它允许一个类(子类或派生类)继承另一个类(父类或基类)的属性和方法。通过继承,子类可以重用父类的代码,并添加自己的特定功能。
- 多态(Polymorphism)
- 多态是面向对象编程的三大特性之一,它允许不同的对象对同一消息作出不同的响应。在Java中,这可以通过方法重载(Overloading)和方法重写(Overriding)来实现。
- 抽象(Abstraction)
- 抽象是一种将复杂的现实世界简化为易于理解和处理的模型的方法。在Java中,抽象类和接口是实现抽象的主要方式。抽象类不能被实例化,但可以被继承;接口则完全由抽象方法和常量组成,可以由类实现。
- 扩展性(Extensibility)
- Java具有良好的扩展性,这意味着它可以很容易地适应新的需求和变化。例如,Java的类库和API可以很容易地通过添加新的类或方法来进行扩展。
- 健壮性(Robustness)
- Java具有多种机制来确保程序的健壮性,包括自动内存管理(通过垃圾回收机制)、异常处理、类型检查和运行时检查等。这些机制有助于减少程序中的错误和漏洞。
- 垃圾回收机制(Garbage Collection)
- Java提供了自动的内存管理功能,即垃圾回收机制。当对象不再被引用时,垃圾回收器会自动释放其占用的内存,从而避免了内存泄漏和内存溢出等问题。
- 虚拟机(Virtual Machine)
- Java虚拟机(JVM)是Java平台的核心组成部分,它负责在运行时加载、验证、解释和执行Java字节码。JVM是Java跨平台特性的关键,它使得Java程序能够在任何安装了JVM的平台上运行。
这些特性使得Java成为一种功能强大、易于学习和使用的编程语言,广泛应用于企业级应用、Web开发、移动应用、大数据和云计算等领域。
第三题:变量、注释、关键字、数据类型、选择判断语句、循环语句、数组、break、continue、运算符
变量
变量是用于存储数据值的容器,这些值可以是整数、浮点数、字符、字符串等。在Java中,每个变量都有一个类型,如
int
、double
、char
、String
等。int age = 30; String name = "Alice";
注释
注释是用于解释代码用途的文本,它不会被编译器执行。Java支持三种类型的注释:
- 单行注释:
// 这是一个单行注释
- 多行注释:
/* 这是 一个 多行注释 */
- Javadoc注释:
/** 这是一个Javadoc注释,用于生成API文档 */
关键字
关键字是Java编程语言中预定义的、具有特殊含义的标识符。例如:
int
、class
、for
、if
、else
等。数据类型
Java是一种强类型语言,每个变量都必须声明其数据类型。Java的数据类型包括:
- 基本数据类型:
byte
、short
、int
、long
、float
、double
、char
、boolean
- 引用数据类型:类(class)、接口(interface)、数组(array)
选择判断语句
Java中的选择判断语句主要包括
if
、else
、switch
等。int number = 5; if (number > 0) { System.out.println("Number is positive"); } else { System.out.println("Number is non-positive"); } switch (number) { case 1: System.out.println("One"); break; case 2: System.out.println("Two"); break; default: System.out.println("Other number"); }
循环语句
Java中的循环语句主要包括
for
、while
、do-while
。for (int i = 0; i < 5; i++) { System.out.println(i); } int j = 0; while (j < 5) { System.out.println(j); j++; } int k = 0; do { System.out.println(k); k++; } while (k < 5);
数组
数组是相同类型数据的集合,可以通过索引访问。
int[] numbers = {1, 2, 3, 4, 5}; for (int i = 0; i < numbers.length; i++) { System.out.println(numbers[i]); }
break 和 continue
break
:用于完全终止循环。continue
:用于跳过当前循环的剩余部分,直接进入下一次循环。for (int i = 0; i < 10; i++) { if (i == 5) { break; // 当i等于5时,跳出循环 } if (i % 2 == 0) { continue; // 跳过偶数 } System.out.println(i); // 打印奇数(除了5) }
运算符
Java支持多种运算符,包括算术运算符、关系运算符、逻辑运算符、位运算符、赋值运算符等。
int a = 5; int b = 10; System.out.println(a + b); // 加法运算符 System.out.println(a > b); // 关系运算符,返回false System.out.println(a < b && a != 0); // 逻辑运算符,返回true
面向对象与类的高级属性
类与对象
面向对象概念
- 面向过程
在一个结构体中定义窗口的大小,位置,颜色,背景等属性,对窗口操作的函数与窗口本身的定义没有任何关系,如HideWindow,MoveWindow,MinimizeWindow,这些函数都需要接收一个代表要被操作的窗口参数,是一种谓语与宾语的关系。中心在实现问题或完成的功能的具体过程中
- 面向对象
定义窗口时,除了要指定在面向过程中规定的那些属性,如大小,位置,颜色,背景等外,还要指定该窗口可能具有的动作,如隐藏,移动,最小化等。这些函数被调用时,都是以某个窗口要隐藏,某个窗口要移动的语法格式来使用的,这是一种主语与谓语的关系。重在对象,只需要知道什么对象有什么方法使用即调用即可
- 面向对象的三大特征
- 封装
- 继承
- 多态
面向对象思想
- 面向对象的编程思想力图使计算机语言中对事物的描述与现实世界中该事物的本来面目尽可能的一致。
- 类(class)和对象(object)是面向对象方法的核心概念。类是对一类事物描述,是抽象的、概念上的定义;对象是实际存在的该类事物的每个个体,因而也称实例(instance)。
- 类是一个模板,对象是模板的具体实例
类与对象
方法是为属性服务的,类包含属性和方法
方法:实现一定功能或者达到某种目的的代码片段,抽取了公共的相同的需求封装成一个方法
eg:
Animal |
---|
+leg:int |
+eat():void +move():void |
public class Animal{
public int legs;
public void eat{
System.out.println("Eating!!!");
}
public void move{
System.out.println("Moving!!!");
}
}
//定义一个类
Animal cat = new Animal();
//声明并创建一个Animal类的对象,将该对象名改为cat
cat.eat();
cat.move();
//调用对象cat中的方法
类的定义
两部分组成:属性、方法
1、声明属性:
语法格式:
[<修饰符>]类型<属性名>[=初值]:
说明:修饰符private:该属性只能由该类的方法访问。
修饰符public:该属性可以被该类以外的方法访问。
类型:任何基本类型,如int、boolean或任何类。
举例1:
oublic class Person{
private int age;//声明private变量age
public String name="xiaozhao";//声明public变量name
}
2、声明方法
语法格式:
<修饰符><返回类型><方法名>([<参数表>]){ [<语句>] }
说明:修饰符:public,,private,protected等。
返回类型:return语句传递返回值。没有返回值:void。
public class Person{
private int age;
public int getAge(){return age; }//声明方法getAge
public void setAge(int i){//声明方法setAge
age=i;//将参数i的值赋值给成员变量age
}
}
//方法有时也称为:成员函数(函数)
3、声明类
语法格式:
[<修饰符>]class<类名> [<属性声明>] [<构造器声明>] [<方法声明>]
说明:修饰符public:类可以被任意访问
类的正文要用{}括起来
注意:类名必须要与文件名完全一致,一个文件中可以存在多个类,但是只能用一个类被public所修饰
public class Animal{
public int legs;
public void eat(){
System.out.println("Eating!!!");
}
public void move(){
System.out.println("Moving!!!");
}
}
legs是类的属性,也叫类成员变量
eat,move是方法也叫类的成员函数
public class Person{
private int age;//声明私有变量age
public void showAge(int i){
age=i;//声明方法showAge
}
}
对象的创建与使用
使用new+构造方法 创建一个新的对象
使用对象名.对象成员的方式访问对象成员(包括属性和方法)
public class Animal{
public int legs;
public void eat(){
System.out.println("Eating!!!");
}
public void move(){
System.out.println("Moving!!!");
}
}
public class zoo{
public static void main(String args[]){
Animal.cat=new Animal();
cat.legs=4;
System.out.println(cat.legs);
cat.eat();
cat.move();
}
}
如果创建了一个类的多个对象,对于类中定义的属性,每个对象都拥有各自的一套副本,互补干扰
public class zoo{
public static void main(String args[]){
Animal cat=new Animal();
Animal dog=new Animal();
cat.legs=4;
dog.legs=4;
System.out.println(cat.legs);
System.out.println(dog.legs);
}
}
类的访问机制:
在一个类中的访问机制:类中的方法可以直接访问类中的成员变量(有一个例外)
在不同的类中的访问机制:先创建要访问类的对象,再用对象访问类中定义的成员
总结:类中的属性和方法虽然是公共的,但是具体出来的对象的属性和方法都是独立的(对象的数据相互隔离)
对象的产生
class Person{
int age;
void shout(){
System.out.println("oh,my god! I am + "age");
}
}
Person p1 = new Person();//执行完后的内存状态
当一个对象被创建时,会对其中各种类型的成员变量自动进行初始化赋值。除了基本数据类型之外都是变量类型、引用类型
对象的使用
创建新的对象之后,我们就可以使用对象名.对象成员的格式来访问对象的属性
class TestPerson{
public static void main(String[] args){ //上面程序运行的内存布局如下图
Person p1 = new Person();
Person p2 = new Person();
p1.age=30;
p2.shout();
}
}
对象的声明周期
匿名对象
我们也可以不定义对象的句柄,而直接调用这个对象的方法。这样的对象叫做匿名对象,如:new Person().shout();
如果对一个对象只需要进行一次方法调用,那么就可以使用匿名对象。
我们经常将匿名对象作为实参传递给一个函数调用。
作业2
利用面向对象的编程方法,设计类Circle计算圆的面积,提示属性包括半径R,圆周率3.14
作业代码
package com.ms08067;
//测试类
public class CircleTest {
public static void main(String[] args) {
Circle c1 = new Circle();
c1.radius = 2;
//对应方式一:
// double area = c1.findArea();
// System.out.println(area);
//对应方式二:
c1.findArea();
}
}
//圆
class Circle{
//属性
double radius;
double PI=3.14;
//求圆的面积
//方式一:
// public double findArea(){
// double area = Math.PI * radius * radius;
// return area;
// }
//方式二:
public void findArea(){
double area = PI * radius * radius;
System.out.println("面积为:" + area);
}
}
信息封装与隐藏
必要性
使用者对类内部定义的属性(对象的成员变量)的直接操作会导致数据的错误、混乱或安全性问题
public class Animal{
public int legs;
public void eat(){
System.out.println("Eating!!!");
}
public void move(){
System.out.println("Moving!!!");
}
}
public class Zoo{
public static void main(String args[]){
Animal.cat=new Animal();
cat.legs=4;
System.out.println(cat.legs);
cat.eat();
cat.move();
}
}
应该将legs属性保护起来,防止乱用。保护方式:信息隐藏
private关键字
Java中通过数据声明为私有的(private),再提供公开的(public)方法:getXXX和setXXX实现对该属性的操作,以实现下述目的:
隐藏一个类的细节;
使用者只能通过实现定制好的方法来访问数据,可以方便的加入控制逻辑,限制对属性的不合理操作
便于修改增强代码的可维护性
public class Animal{
private int legs;
public voud setLegs(int i){
if(i!=0&&i!=2&&i!=4){
System.out.println("Wrong number of legs!");
return;
}
legs=i;
}
public int getLegs(){
return legs;
}
}
public class Zoo{
public static void main(String args[]){
Animal.cat = new Animal();
cat.setLegs(4);
cat.setLegs(-1000);//非法
System.out.println(cat.getLegs());
}
}
作业3
创建程序,在其中定义两个类,Person和TestPerson类定义如下,用SetAge()设置人的合法年龄(0-130),用getAge()返回人的年龄。在Test类中实例化Person类的对象b,调用setAge()方法和getAge方法
Person |
---|
-age:int |
+setAge(i:int) +getAge():int |
作业代码
public class Person{
private int age;
public void setAge(int i){
if(i<0||i>130){
System.out.println("Wrong number of age!");
return;
}
age=i;
}
public int getAge(){
return age;
}
}
public class TestPerson{
public static void main(String args[]){
Person.zz=new Person();
zz.age(19);
System.out.println(zz.getAge());
}
}
构造方法(构造器)
构造器的定义、作用
需要注意的是:当new的时候其实就是调用了需要创建对象的类的构造器
1.构造方法的特征
它具有与类相同的名称;
它不含返回值:
注意:在构造方法里不含返回值的概念是不同于“vod”的,在定义构造方法时加了“void”,结果这个方法就不再被自动调了
2.构造方法的作用
当一个类的实例对象刚产生时,这个类的构造方法就会被自动调用,我们可以在这个方法中加入要完成初始化工作的代码。这就好像我们规定每个“人”一出生就必须先洗澡,我们就可以在“人”的构造方法中加入完成“洗澡”的程序代码,于是每个“人”一出生就会自动完成“洗澡”,程序就不必再在每个人刚出生时一个一个地告诉他们要“洗澡”了。
注意:
(1)如果手动创建一个含参构造器,就必须手动的创建一个无参构造器,不然的话就会出现编译错误
(2)默认不创建构造器,系统在编译类的时候也会自动创建一个无参构造器
表现形式
功能:创建类的实例时,初始化实例的一组指令。
语法格式:
<修饰符><类名>([<参数表>]){ [<语句>] }
举例:
public class Animal{
private int legs;
public Animal(){legs=4;}
public void setLegs(int i){legs=i;}
public int getLegs(){return legs;}
}
创建Animal类的实例:Animal a = new Animal();将调用构造器,将legs初始化为4
注释:构造器的名称必须与类名相同。修饰符:public、private、protected
构造器不是一般的方法,没有返回值(连void也不能写)
默认构造方法
Java语言中,每个类都至少有一个构造方法;
如果类的定义者没有显式的定义任何构造方法,系统将自动提供一个默认的构造方法:
(1)默认构造方法没有参数
(2)默认构造方法没有方法体
(3)默认的构造方法:Animal(){}
所以:不编写构造方法就能用new Xxx()创建类的实例。
Java类中,一旦类的定义者显式定义了一个或多个构造方法,系统将不再提供默认的构造方法;
构造器的主要作用:利用构造器参数初始化对象的属性。
创建对象内存分析
Person p = new Person(“Tom”, 18)的内存状态变化过程分析
方法的重载
- 发生在同一类
- 方法名完全相同
- 和返回类没有关系
- 参数列表不相同(参数的个数、类型、顺序)
方法的定义
返回值类型函数名(参数类型 形式参数1,参数类型 形式参数2,...)
{
程序代码
return返回值;
}
形式参数:在方法被调用时用于接收外部传入的数据变量
参数类型:就是该形式参数的数据类型
返回值:方法在执行完毕后返还给调用它的程序的数据
返回值类型:函数要返回的结果的数据类型
实参:调用参数时实际传给函数形式参数的数据
方法的调用
函数调用的过程分析
方法的重载
函数的重载就是在同一个类中允许同时存在一个以上的同名函数,只要他们的参数个数或类型不同即可
public class Test{
public static void main(Stringp[] args){
int isum;
double fsum;
isum=add(3,5);
isum=add(3,5,6);
fsum=add(3.2,6.5);
}
public static int add(int x,int y) {return x+y;}
public static int add(int x,int y,int z) {return x+y+z;}
public static double add(double x,double y) {return x+y;}
}
在同一个类中可以定义多个方法-方法名重载
public class PrintStream{
public void print(int i){......}
public void print(float f){......}
public void print(String s){......}
}
重载方法的参数列表必须不同
重载方法的返回值类型可以相同,也可以不同(无关)
调用时根据方法的参数类型来区别。
print(3);print(1.2f);print(“hello!”);
作业4
对代码进行优化,越少越好,实现clc弹窗计算器的两个数的操作,考虑边界问题,例如除数不为0等等,尽量实现通过选项的形式操作运算符,通过选项的形式或者是识别处理字符自动匹配使其各个重载的方法都能用调用。
作业代码
public class Calculator {
public int add(int num1, int num2) {
return num1 + num2;
}
public int subtract(int num1, int num2) {
return num1 - num2;
}
public int multiply(int num1, int num2) {
return num1 * num2;
}
public double divide(double num1, double num2) {
if (num2 != 0) {
return num1 / num2;
} else {
throw new ArithmeticException("Division by zero is not allowed.");
}
}
public static void main(String[] args) {
Calculator calculator = new Calculator();
int additionResult = calculator.add(5, 3);
System.out.println("Addition: " + additionResult);
int subtractionResult = calculator.subtract(8, 4);
System.out.println("Subtraction: " + subtractionResult);
int multiplicationResult = calculator.multiply(2, 6);
System.out.println("Multiplication: " + multiplicationResult);
try {
double divisionResult = calculator.divide(10, 2);
System.out.println("Division: " + divisionResult);
} catch (ArithmeticException e) {
System.out.println(e.getMessage());
}
}
}
this关键字
定义
当前对象
如果func2方法被调用,一定是事先已经有了一个存在的对象,func2被作为那个对象的方法被使用。在func2内部能引用别的对象,同样也能引用func2所属的那个对象
在func2中,自己所属的那个对象的引用名称是什么?
this关键字在java程序里的作用和它的词义很接近,它在函数内部就是这个函数所属的对象的引用变量
引用
每个成员方法内部,都有一个this引用变量,指向调用这个方法的对象,类中的成员与this之间的关系
import语句与package语句
源文件布局 1、包名 2、导入的包
package
包帮助管理大型软件系统:将语义近似的类组织到包中
包可以包含类和子包
思想:大量调用三方API,能自己不写就不写
package语句
package语句作为Java源文件的第一条语句,指名该文件中定义的类所在的包(若缺省该语句,则指定为无名包)格式为
package <顶层包名>[.<子包名>]*;
package p1;
public class Test{
public void display(){
System.out.println("in method display()");
}
}
包对应于文件系统的目录,package语句中,用‘.’来指明包的层次
包通常用小写单词,类名首字母通常大写
import语句
为使用定义在不同包中的java类,需用import语句来引用所需要的类。import语句告诉编译器到哪里去寻找类
语法格式:
import 包名[.子包名...].<类名|*>
应用举例
import p1.Test; //import p1.*表示引用p1包中的所有类
public class TestPackage{
public static void main(String args[]){
Test t=new Test;
t.display;
}
}
类的继承
父类:为描述和处理个人信息,定义类Person
public class Person{
public String name;
public int age;
public Date birthDate;
public String getInfo();
{...}
}
子类:为描述和处理学生信息,定义类Student
public class student{
public String name;
public int age;
public Date birthDate;
public String school;
public String getInfo();
{...}
}
类继承语法规则:
<修饰符>class<子类名称>[extends<父类>]
{
<属性和方法的声明>
}
Java只支持单继承,不允许多重继承
一个子类只能有一个父类
一个父类可以派生出多个子类
关于继承的规则:
子类不能继承父类中私有的(private)的成员变量和方法。
通过继承,简化Student类的定义
public class Person{
public String name;
public int age;
public Date birthDate;
public String getInfo();
{...}
}
public class Student extends Person{
public String school;
}
//student类继承了父类Person的所有属性和方法,并增加一个属性school Person中的属性和方法,student都可以利用
访问控制
可以对Java类中定义的属性和方法及类自身进行访问控制---规定不同的保护等级:public protected default private
修饰符 | 同一个类 | 同一个包 | 子类 | 整体 |
---|---|---|---|---|
private | yes | no | no | no |
default | yes | yes | no | no |
protected | yes | yes | yes | no |
public | yes | yes | yes | yes |
class Parent{
private int f1=1;
int f2=2;
protected int f3=3;
public int f4=4;
private void fm1(){
System.out.println("in fm1() f1="+f1);
}
void fm2(){
System.out.println("in fm2() f2="+f2);
}
protected void fm3(){
System.out.println("in fm3() f3="+f3);
}
public void fm4(){
System.out.println("in fm4() f4="+f4);
}
}
class Child extends Parent{
private int c1=21;
public int c2=22;
private void cm1(){ System.out.println("in cm1() c1="+c1);}
public void cm2(){ System.out.println("in cm2() c2="+c2);}
public static void main(String args[]){
int i;
Parent p=new Parent();
i=p.f2;//i=p.f3; i=p.f4; p.fm2(); //p.fm3();p.fm4();
Child c = new Child();
i=c.f2;//i=c.f3;i=c.f4;
i=c.c1;//i=c.c2;
c.cm1;//c.cm2();c.fm3();c.fm4()
}
}
访问控制分析
父类Parent和子类Child在同一个包中定义时
方法的重写
发生在父类和子类之间、方法相同(返回值类型、参数列表、方法名称)只有方法体不一样才可以
- 在子类中可以根据需要对从父类中继承来的方法进行改造-覆盖方法(方法的重置、重写),在程序执行时,子类的方法将覆盖父类的方法
- 覆盖方法必须和被覆盖方法具有相同的名称、参数列表和返回值类型
- 覆盖方法不能使用比被覆盖方法更严格的访问
Person p1 = new Person();
p1.getInfo();
Student s1 = new Student();
s1.getInfo();
//这是一种多态性:同名的方法,用不同的对象来区分调用的是哪一个方法
public class Student extends Person{
public String school;
public String getInfo();{
return "Name:"+name+"\nage:"+age+"\nschool:"+school;
}
public static void main(String args[]){
Student s1 = new Student();
s1.name="Bob";
s1.age=20;
s1.school="清华大学";
System.out.println(s1.getInfo());
}
}
super关键字
关键字super代表父对象,在java类中使用super来引用父类的成分
- super可用于访问父类中定义的属性
- super可用于调用父类中定义的成员方法
- super可用于在子类构造方法中调用父类的构造方法
- super的追溯不仅限于直接父类
public class Person{
private String name;
private int age;
public String getInfo(){
return "Name:"+ name+"\nage:"+age;
}
}
public class Student extends Person{
private String school = "北京大学";
public String getSchool(){return school;}
public String getInfo();
return super.getInfo()+"\nschool:"+school;
}
- 构造方法不能继承
子类继承父类所有的成员变量和成员方法,但不继承父类的构造方法
在一个Java类中可以通过两种方式获得构造方法
使用系统默认的无参数构造方法
显式定义一个或多个构造方法
一旦显式定义了构造方法,则系统不再提供默认构造方法
- 调用父类构造方法
在子类的构造方法中可使用super(参数列表)语句调用父类的构造方法
如果子类的构造方法中没有显示地调用父类构造方法,也没有使用this关键字调用重载的其它构造方法,则系统默认调用父类无参数的构造方法
如果子类构造方法中既未显式调用父类构造方法,而父类中又没有无参的构造方法,则编译出错
public class Person{
private String name;
private int age;
private Date birthDate;
public Person(String name,int age,Date d){
this.name=name;
this.age=age;
this.birthDate=d;
}
public Person(String name,int age){
this(name,age,null);
}
public Person(String name,Date d){
this(nam,30,d);
}
public Person(String name){
this(name,30);
}
}
多态性
- 多态---在java中,子类的对象可以替代父类的对象使用
- 一个变量只能有一种确定的数据类型
- 一个引用变量可能指向(引用)多种不同类型的对象
- 父类类型的变量可以指向子类的对象
一个引用类型变量如果声明为父类的类型,但实际引用的是子类对象,那么该变量就不能再访问子类中添加的属性和方法
正常方法调用
Person e = new Person();
e.getInfo();
Student e = new Student();
e.getInfo();
虚拟方法调用(多态情况下)
Person e = new Student();
e.getInfo;//调用Student类的getInfo方法
编译时类型和运行时类型
编译时e为Person类型,而方法的调用是在运行时确定的,所以调用的是Student类的getInfo0方法(动态绑定)
方法声明的形参类型为父类类型,可以使用子类的对象作为实参调用该方法
public class Test{
public void method(Person e){
//...
e.getInfo();
}
public static void main(String args[]){
Test t = new Test();
Student m = new Student();
t.method(m);
}
}
instance操作符
x instanceof A:检验x是否为类A的对象,返回值为boolean型。
要求x所属的类与类A必须是子类和父类的关系,否则编译错误。
如果x属于类A的子类B,x instanceof A值也为true。
public class Person extends Object {...}
public class Student extends Person {...}
public class Graduate extends Person {...}
public void method1(Person e){
if (e instanceof Person)
//处理Person类其子类对象
if (e instanceof Stident)
//处理Student类其子类对象
if (e instanceof Graduate)
//处理Graduate类其子类对象
}
对象类型转换
1、基本数据类型的Casting:
(1)小的数据类型可以自动转换成大的数据类型,如logg=20: double d=12.0f
(2)可以把大的数据类型强制转换(casting)成小的数据类型。如f1 oate f=((float)12.0inta=(int)1200L
2、对ava对象的强制类型转换称为造型
(1)从子类到父类的类型转换可以自动进行
(2)从父类到子类的类型转换必须通过造型(强制类型转换)实现
(3)无继承关系的引用类型间的转换是非法的
(4)在造型前可以使用instanceoff操作符测试一个对象的类型
public class Test{
public void method(Person e){
//设Person类中没有getschool()方法
System.out.pritnln(e.getschool());/非法,编译时错误
if(e instanceof Student){
Student me=(Student)e;//将e强制转换为Student类型
System.out.pritnln(me.getschool());
}
public static void main(Stirng args[]){
Test t new Test();
Student m new Student();
t.method(m);
}
}
Object类
Object类是所有)java类的根父类
如果在类的声明中未使用extends关键字指明其父类,则默认父类为Object类
public class Person{
...
}
等价于
public class Person extends Object{
...
}
equals方法与==
引用类型比较引用(是否指向同一个对象)
Person p1 = new Person();
Person p2 = new Person();
if(p1==p2){...} true 或者 false
基本类型比较值
int a=5 if(a==6){...}
- 用“==“"进行比较时,符号两边的数据类型必须一致(可自动转换的基本数据类型除外),否则编译出错;
- equals0方法是Object类的方法,由于所有类都继承Object类,也就继承了equals0方法。只能比较引用类型,其作用与“==”相同,比较是否指向同一个对象。格式:obj1.equals(obj2)
- 特例:当用equals()方法进行比较时,对类File、String、Date及封装类(Wrapper Class)来说,是比较类型及内容而不考虑引用的是否是同一个对象;
- 原因:在这些类中覆盖了equals(0方法。
toString
- toString()方法再Object类中定义,其返回值是String类型,返回类和它的引用地址
- 在进行String与其他类型数据的连接操作时,自动调用toString方法
Date now = new Date();
System.out.println(now);
//相当于System.out.println(now.toString());
- 可以根据需要在用户自定义类型中重写toString0方法,如String类重写了toString0方法,返回字符串的值。
- 在ToString1,java中的类A里覆盖toString方法,使其输出类A对象的cint属性值。
- 基本类型数据转换为String类型时,调用了对应封装类的toString0方法inta=10;System.out.println(“a=”+a);
封装类
基本数据类型 | 封装类 |
---|---|
boolean | Boolean |
byte | Byte |
short | Short |
int | Integer |
long | Long |
char | Character |
float | Float |
double | Double |
static关键字
- Java类中声明变量、方法和内部类时,可使用关键字static作为修饰符
- static标记的变量或方法由整个类(所有实例)共享,如访问控制权限允许,可不必创建该类对象而直接用类名加‘.’调用
- static成员也该称类成员或静态成员,如:类变量、类方法、静态方法等
类属性、类方法的涉及思想
- 类属性作为该类各个对象之间共享的变量。在设计类时,分析哪些类属性不因对象的不同而改变,将这些属性设置为类属性。相应的方法设置为类方法
- 如果方法与调用者无关,则这样的方法通常被声明为类方法,由于不需要创建对象就可以调用类方法,从而简化了方法调用
变量(类属性)
由该类的所有实例共享
类方法
- 静态方法里只能直接调用同类中其他的静态成员(包括变量和方法),而不能直接访问类中的非静态成员。这是因为,对于非静态的方法和变量,需要先创建类的实例对象才可使用,而静态方法在使用前不用创建任何对象
- 静态方法不能以任何方式引用this和super关键字,与上面的道理一样,因为静态方法在使用前不用创建任何实例对象,当静态方法被调用时,this所引用的对象根本就没有产生
- main()方法是静态的,因此JVM在执行main方法时不创建main方法所在的类的实例对象,因而在main()方法中,我们不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员。
没有对象的实例时,可以用类名.方法ming()的形式访问由static标记的类
在static方法内部只能访问类的static属性,不能访问类的非static属性
class Person{
private int id;
private static int total=0;
public static int getTontalPerson(){
id++;//非法
return total;
}
public Person(){
total++;
id=total;
}
}
因为不需要实例就可以访问static方法,因此static方法内部不能有this
class Person{
private int id;
private static int total=0;
public static void setTotalPerson(int total){
this.total=total;//非法 在static方法中不能有this,也不能有super
}
public Person{
total++;
id=total;
}
}
public class TestPerson{
public static void main(String[] args){
Person.setTotalPerson();
}
}
静态初始化
- 一个类中可以使用不包含任何方法体中的静态代码块(static block),当类被载入时,静态代码被执行,且只被执行一次,静态块经常用来进行类属性的初始化
- static块通常用于初始化static(类)属性
class Person{
public static int total;
static{
total=100;//为total赋初值
}
...//其它属性或方法声明
}
final关键字
- 在Java中声明类、属性和方法时,可使用关键字final来修饰
- final标记的变量(成员变量或局部变量)即成为常量,只能赋值一次
- final标记的类不能被继承。提高安全性,提高程序的可读性
- final标记的方法不能被子类重写,增加安全性
- final标记的成员变量必须在声明的同时或在每个构造方法中显示赋值,然后才能使用
final PI=3.14;
抽象类
- 随着继承层次中一个个新子类的定义,类变得越来越具体,而父类则更一般,更通用。类的设计应该保证父类和子类能够共享特征。有时将一个父类设计得非常抽象,以至于它没有具体的实例,这样的类叫做抽象类。
- 用abstract关键字来修饰一个类时,这个类叫做抽象类;用abstract来修饰一个方法时,该方法叫做抽象方法。
- 抽象方法:只有方法的声明,没有方法的实现。以分号结束。
- abstract int abstractMethod1(int a );
- 含有抽象方法的类必须被声明为抽象类。
- 抽象类不能被实例化。抽象类是用来被继承的,抽象类的子类必须重写父类的抽象方法,并提供方法体。
- 不能用abstract修饰私有方法,构造方法,静态方法。
abstract class A{
abstract void m1();
public void m2(){
System.out.println("A类中定义的m2方法");
}
}
class B extends A{
void m1(){
System.out.println("B类中定义m1方法");
}
void m3(){
==========
}
public class Test{
public static void main(String args[]){
A c = new B(); B c = new B(); b.m3() c.m3();
c.m1();
c.m2();
}
}
}
抽象类是用来模型化哪些父类无法确定全部实现,而是由其子类提供具体实现的对象的类
在航运公司系统中,Vehicle类需要定义两个方法分别计算运输工具的燃料效率和行驶距离。
1、问题:卡车(Truck)和船(RiverBarge)的燃料效率和行驶距离的计算方法完全不同。Vehicle类不能提供计算方法,但子类可以。
2、解决方案:
jva允许类设计者指定:超类声明一个方法但不提供实现,该方法的实现由子类提供。这样的方法称为抽象方法。有一个或更多抽象方法的类称为抽象类。Vehicle是一个抽象类,有两个抽象方法。
public abstract class Vehicle{
public abstract double calcFuelEfficiency();//计算燃料效率的抽象方法
public abstract double calcTripDistance();//计算行驶举例的抽象方法
}
public class Truck extends Vehicle{
public double calcFuelEfficiency();//写出计算卡车的燃料效率的具体方法
public double calcTripDistance();//写出计算卡车行驶举例的具体方法
}
public class RiverBarge extends Vehicle{
public double calcFuelEfficiency();//写出计算驳船的燃料效率的具体方法
public double calcTripDistance();//写出计算驳船行驶举例的具体方法
}
接口(interface)
接口定义
接口是一种规范和标准,有时必须从几个类中派生出一个子类,继承它们所有的属性和方法。但是,Java不支持多重继承。有了接口,就可以得到多重继承的效果。换句话说,接口的优势之一是为了弥补java单继承的缺陷和不足。
接口(interface)是抽象方法和常量值的定义的集合。
从本质上讲,接口是一种特殊的抽象类,这种抽象类中只包含常量和方法的定义,而没有变量和方法的实现。
定义java类的语法格式
[implements
}
接口定义举例
public interface Runner{
int id=1;
public void start();
public void run();
public void stop();
}
接口特点
- 用interface来定义
- 接口中的所有成员变量都默认由public static final修饰的
- 接口中的所有方法都默认是由public abstract修饰。接口没有构造方法
- 接口也可以继承另一个接口,使用extends关键字
- 实现接口的类中必须提供接口中所有方法的具体实现内容
- 多个无关的类可以实现同一个接口
- 一个类可以实现多个无关的接口
- 与继承管理类似,接口与实现类
public interface Runner{
public void start();
public void run();
public void stop();
}
public class Person implements Runner{
public void start();{
....
}
public void run(){
....
}
public void stop(){
....
}
}
第二章练习
java集合
回忆数组概念:
- 存储相同类型的元素或者是变量
- 索引(下标)从0开始并且在创建数组的时候需要先确定数组的长度(存储变量的个数)
- 数组的意义就在于能够将一个内存空间化为等量对小区间,每一个区间都用以存放相同类型的数据,以节省内存空间的同时便于管理数据和变量
数组的局限性:只能存放相同类型的数据,数据定义之后长度不能改变(存储变量的个数固定)
解决局限性采用集合
- Java集合就像一种容器,可以把多个对象的引用放入容器中
- Java集合类可以用于存储数量不等的多个对象,还可用于保存具有映射关系数组
- Java集合可分为Set、List和Map三种体系
- Set:无序、不可重复集合
- List:有序、可重复集合
- Map:具有映射关系的集合 key value
String id = request.GetParmter(“id”);
假如前端的id被恶意拼接------>id'
Map while={"username":"username","password":"password","address":"address","id":"id"}/规定白名单
Map black="所有的sql语句的关键字,update,database(),',"//黑名单
String value map.getValueByKey(id);
String value map.getValueByKey(id);
if (value == null){
return null;
elsef
select from user where id
}
select from user where id #id
- 在Java5之前,Java集合会丢失容器中所有对象的数据类型,把所有对象都当成Object类型处理;
- 从Java5增加了泛型以后,Java集合可以记住容器中对象的数据类型
虽然集合允许存储不同数据类型的变量,但是为了安全起见,一般我们存储的相同类型的变量
数组和集合的区别:
(1)数组是定义时必须规定长度,而集合定义无须定义长度,长度可以动态的扩展
(2)数组只能存储相同类型的变量,而集合可以存储任意数据类型的变量
所以,我们在编码中经常应用的集合,但是数组有时候也会使用
思想问题:假如你是jav定义集合的架构师,你觉得集合应该提供哪些方法满足日常使用
(1)添加元素方法(2)获取元素:a:根据元素的值获取b:根据索引获取对应的元素(3)删除元素(4)修改元素(5)获取集合的长度
Set集合
- St集合不允许包含相同的元素,如果试把两个相同的元素加入同一个Set集合中,则添加操作失败
- Set判断两个对象是否相同不是使用==运算符,而是根据equals方法
- 对于引用数据类型比较的是地址
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.ms08067.collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class Demo01 {
public Demo01() {
}
public static void main(String[] args) {
Set set = new HashSet();
set.add("高12");
set.add("高3");
set.add("高1");
set.add("高6");
set.add("高8");
set.add("高2");
set.add("高4");
set.add("高4");
set.add("高4");
System.out.println("获取set集合元素的个数:" + set.size());
System.out.println("获取集合中是否包含某个元素:" + set.contains("高1"));
System.out.println("判断集合是为空集合:" + set.isEmpty());
System.out.println("移除集合中的某一个元素:" + set.remove("高4"));
Iterator iter = set.iterator();
while(iter.hasNext()) {
String str = (String)iter.next();
System.out.println(str);
}
}
}
练习一
list集合
-
List代表一个元素有序、且可重复的集合,集合中的每个元素都有其对应的顺序索引
-
List允许使用重复元素,可以通过索引来访问指定位置的集合元素
-
List默认按元素的添加顺序设置元素的索引
-
List集合里添加了一些根据索引来操作集合元素的方法
-
Arraylist和LinkedList集合的区别和联系
- 底层实现的逻辑不一样:
- Arraylist底层是使用了数组的逻辑,数组是基于顺序存储的线性结果,也就是说逻辑地址和物理地址均是相邻的
- LinkListed底层是基于链式存储的,逻辑地址和物理地址不相邻
- 结合底层逻辑特征如下:
- ArrayList.适用于频繁索引,对数据进行增加和删除的操作时(add和remove操作)效率低
- LinkedList适用于对数据结果修改、新增、删除,查找数据效率比较低
因集合实际情况应用,但开发常见的还是使用ArrayList比较多,可能开发中实际查找数据相对比较多些吧
- 底层实现的逻辑不一样:
-
常见list的方法测试
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.ms08067.collection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
public class Demo03 {
public Demo03() {
}
public static void main(String[] args) {
List list = new ArrayList();
list.add("a2");
list.add("b3");
list.add(1);
List list1 = new LinkedList();
list1.add("C2");
list1.add("D3");
list1.add("A1");
for(int i = 0; i < list.size(); ++i) {
System.out.println(list.get(i));
}
System.out.println("toString:" + list.toString());
System.out.println("将List集合转换为数组:" + Arrays.toString(list.toArray()));
System.out.println("判断集合中是否含有某元素:" + list.contains("b3"));
System.out.println("查找指定元素的索引位置:" + list.indexOf("a1"));
System.out.println("判断list集合是否为空" + list.isEmpty());
System.out.println("/获取list集合中相同元素最后一次出现的索引位置:" + list.lastIndexOf("a1"));
System.out.println("获取list集合的迭代器:" + list.iterator());
}
}
练习二
Map集合
- Map用于保存具有映射关系的数据,因此Map集合里保存着两组值,一组用于保存Map里的Key,另外一组用于保存Map里的Value
- Map中的key和value都可以是任何引用类型的数据
- Map中的key不允许重复,即同一个Map对象的任何两个key通过equals方法比较中返回false
- key和value之间存在单向一对一关系,即通过指定的key总能找到唯一的value
HashMap & Hashtable
- HashMap和Hashtable是Map接口的两个典型实现类
- Hashtable是一个古老的Map实现类,不建议使用
- Hashtable是一个线程安全的Map实现,但HashMap是线程不安全的
- Hashtable不允许使用nul作为key和value,而HashMap可以
- 与HashSet集合不能保证元素的顺序的顺序一样,Hashtable、HashMap也不能保证其中key-value对的顺序
- Hashtable、HashMap判断两个Key相等的标准是:两个Key通过equals方法返回true,hashCode值也相等
- Hashtable、HashMap判断两个Value相等的标准是:两个Value通过equals方法返回true
LinkedHashMap
- LinkedHashMap是HashMap的子类
- LinkedHashMap可以维护Map的迭代顺序:迭代顺序与Key-Value对的插入顺序一致
TreeMap
-
TreeMap存储Key-Value对时,需要根据Key对key-value对进行排序。TreeMap可以保证所有的Key-Value对处于有序状态
-
TreeMap的Key的排序:
自然排序:TreeMap的所有的Key必须实现Comparable接口,而且所有的Key应该是同一个类的对象,否则将会抛出ClasssCastException
定制排序:创建TreeMap时,传入一个Comparator对象,该对象负责对TreeMap中的所有key进行排序。此时不需要Map的Key实现Comparable接口
练习三
练习四
作业1
IO流
输入:读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中
输出:将程序(内存)数据输出到磁盘、光盘等存储设备中
Java的IO流主要包括输入、输出两种IO流,每种输入输出流有可分为字节流和字符流两大类:
字节流:以字节为单位来处理输入、输出操作
字符流:以字符为单位来处理输入、输出操作
File类
File类代表与平台无关的文件和目录
File能新建、删除、重命名文件和目录,但File不能访问文件内容本身。如果需要访问文件内容本身,则需要使用输入输出流
IO流的分类
- 按流向分,输入流、输出流
- 按处理的单位,字节流(8位的字节)、字符流(16位的字节)
- 按流的角色,节点流:可以从一个特定的IO设备读/写数据的流,处理流:对一个已存在的流进行连接和封装,通过封装后的流来实现数据读/写操作
InputStream & Reader
-
InputStream和Reader是所有输入流的基类
-
InputStream(典型实现:FileInputStream):
int read()
int read(byte[] b)
int read(byte[] b,int off,int len)
-
Reader(典型实现:FileReader)
int read()
int read(char [] c)
int read(char [] c,int off,int len)
-
程序打开的文件IO资源不属于内存里的资源,垃圾回收机制无法回收该资源,所以应该显式关闭文件IO资源
OutputStream & Writer
-
OutputStream和Writer也非常相似:
void write(byte write/int c)
void []/char[] buff)
void write(byte[]/char[] buff,int off,int len);
-
因为字符流直接以字符作为操作单位,所以Write可以用字符串来替换字符数组,即以String对象作为参数
void write(String str);
void write(String str,int off,int len)
对象的反序列化
对象的序列化是将对象保存到磁盘上,或允许在网络中直接传输对象
序列化是RMI(Remote Method Invoke - 远程方法调用)过程的参数和返回值必须实现的机制,而RMI是JAVAEE的基础。因此序列化机制时JAVAEE平台的基础
如果需要让某个对象支持序列化机制,则必须让的类是可序列化的,为了让某个类是可序列化的,该类必须实现两个接口之一 Serializable、Externalizable
使用对象流序列化对象:
-
序列化
若某个类实现了Serializable接口,该类的对象就是可序列化的:
创建一个ObjectOutputStream
调用ObjectOutStream对象的writeObject()方法输出可序列化对象
-
反序列化
创建一个ObjectInputStream
调用readObject()方法读取对象
如果某个类不是基本数据类型或String类型,而是另一个引用类型,那么这个引用类型必须是可序列化的,否则拥有该类型的Field的类也不能序列化
练习
IO流操作整体的思路:
1、建立联系,创建或获取相关的文件
2、选择操作的流对象
3、使用流对象对文件对象通过循环进行读取或写出数据操作
4、保存信息,释放资源、关闭两个流(先关闭输出流,在关闭输入流)
创建文件和目录
import java.io.File;
import java.io.IOException;
public class FileAndDirectoryCreation {
public static void main(String[] args) {
// 创建文件
String filePath = "path/to/your/file.txt";
File file = new File(filePath);
try {
if (file.createNewFile()) {
System.out.println("文件创建成功:" + file.getAbsolutePath());
} else {
System.out.println("文件已存在:" + file.getAbsolutePath());
}
} catch (IOException e) {
e.printStackTrace();
}
// 创建目录
String directoryPath = "path/to/your/directory";
File directory = new File(directoryPath);
if (directory.mkdir()) {
System.out.println("目录创建成功:" + directory.getAbsolutePath());
} else {
System.out.println("目录已存在:" + directory.getAbsolutePath());
}
}
}
在这个例子中,
createNewFile()
方法尝试创建一个新文件,如果文件不存在,它将创建文件并返回true
。如果文件已经存在,它将返回false
。
mkdir()
方法尝试创建一个新目录,如果成功,返回true
,否则返回false
(例如,如果目录已经存在)。如果你需要创建一个包含父目录的目录路径,可以使用mkdirs()
方法。
读取文件与写出文件
字符流(char)读取文件
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class ReadFileWithCharStream {
public static void main(String[] args) {
String filePath = "path/to/your/file.txt";
try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
在这段代码中:
- 我们使用
FileReader
来读取文件内容,它接受一个文件路径作为参数。- 然后,我们使用
BufferedReader
来包装FileReader
,以提供按行读取文件的便利方法。- 使用
try-with-resources
语句来确保BufferedReader
和FileReader
在读取完毕后能够被正确关闭,即使在发生异常的情况下也能保证资源被释放。readLine()
方法用来逐行读取文件内容,直到文件末尾返回null
。- 读取到的每一行内容被输出到控制台。
确保将
filePath
变量替换为你要读取文件的实际路径。注意:字符流适用于读取文本文件,因为它以字符为单位进行读写,适用于国际化文本。对于二进制文件,通常使用字节流(如
FileInputStream
和BufferedInputStream
)来读取。
字符流(char)(含缓冲流)读取文件
使用字符流(包括缓冲流)读取文件的Java代码示例如下:
java
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class ReadFileWithCharStreamBuffered {
public static void main(String[] args) {
String filePath = "path/to/your/file.txt";
try (BufferedReader bufferedReader = new BufferedReader(new FileReader(filePath))) {
String line;
while ((line = bufferedReader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
在这个例子中,我们使用了
BufferedReader
,它是一个字符缓冲流,它包装了另一个字符流(这里是FileReader
),并提供缓冲字符输入以及文本行的高效读取。BufferedReader
的readLine()
方法用于读取文件中的一行文本。请确保将
filePath
变量替换为你要读取文件的实际路径。这段代码将逐行读取文件内容,并将每一行输出到控制台。使用
try-with-resources
语句可以确保BufferedReader
在读取完成后能够自动关闭,即使在读取过程中发生异常时也能保证资源的正确释放。这是一个很好的实践,因为它可以避免资源泄露。注意:字符流(如
FileReader
和BufferedReader
)通常用于读取文本文件,而字节流(如FileInputStream
和BufferedInputStream
)则适用于读取二进制文件。对于文本文件,字符流提供了更好的性能和易用性,因为它们以字符为单位进行读取,并可以处理不同字符编码的文本文件。
使用流实现文件复制功能
字节流
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class FileCopyWithByteStreams {
public static void main(String[] args) {
String sourceFilePath = "path/to/source/file.txt";
String destinationFilePath = "path/to/destination/file.txt";
try (InputStream inputStream = new FileInputStream(sourceFilePath);
OutputStream outputStream = new FileOutputStream(destinationFilePath)) {
byte[] buffer = new byte[4096]; // 使用一个缓冲区来提高复制效率
int bytesRead;
// 循环读取源文件,并将内容写入目标文件
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
System.out.println("文件复制成功!");
} catch (IOException e) {
e.printStackTrace();
System.out.println("文件复制失败:" + e.getMessage());
}
}
}
在这段代码中,我们做了以下几件事:
- 定义了源文件和目标文件的路径。
- 创建了
FileInputStream
来读取源文件,和FileOutputStream
来写入目标文件。- 使用了一个字节数组
buffer
作为缓冲区,以提高文件复制的效率。这里我们选择了4096字节作为缓冲区大小,但这可以根据具体需求和性能进行调整。- 在一个
while
循环中,我们使用inputStream.read(buffer)
方法来读取源文件的内容到缓冲区中,并返回实际读取的字节数。如果返回-1,则表示已经读取到文件末尾。- 使用
outputStream.write(buffer, 0, bytesRead)
方法将缓冲区中的内容写入到目标文件中。注意这里的第三个参数bytesRead
是实际读取的字节数,确保不会写入缓冲区中未使用的部分。- 使用了try-with-resources语句来确保流在使用完毕后能够自动关闭,即使发生异常也能保证资源的正确释放。
请确保将
sourceFilePath
和destinationFilePath
替换为实际的源文件和目标文件路径。
字符流
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class FileCopyWithCharStreams {
public static void main(String[] args) {
String sourceFilePath = "path/to/source/file.txt";
String destinationFilePath = "path/to/destination/file.txt";
try (BufferedReader reader = new BufferedReader(new FileReader(sourceFilePath));
BufferedWriter writer = new BufferedWriter(new FileWriter(destinationFilePath))) {
char[] buffer = new char[4096]; // 使用一个字符数组作为缓冲区
int charsRead;
// 循环读取源文件,并将内容写入目标文件
while ((charsRead = reader.read(buffer)) != -1) {
writer.write(buffer, 0, charsRead);
writer.flush(); // 确保数据被写入目标文件
}
System.out.println("文件复制成功!");
} catch (IOException e) {
e.printStackTrace();
System.out.println("文件复制失败:" + e.getMessage());
}
}
}
在这段代码中,我们执行了以下步骤:
- 定义了源文件和目标文件的路径。
- 创建了
BufferedReader
对象来读取源文件内容,以及BufferedWriter
对象来写入目标文件。- 使用了一个字符数组
buffer
作为缓冲区,以提高文件复制的效率。- 在
while
循环中,使用reader.read(buffer)
方法读取源文件的内容到缓冲区中,并返回实际读取的字符数。- 使用
writer.write(buffer, 0, charsRead)
方法将缓冲区中的内容写入到目标文件中。注意这里的第三个参数charsRead
是实际读取的字符数。- 调用
writer.flush()
方法确保缓冲区中的数据被立即写入目标文件。- 使用
try-with-resources
语句来确保BufferedReader
和BufferedWriter
在使用完毕后能够被正确关闭。请注意,字符流在处理非文本文件(如图片、音频或视频文件)时可能会导致数据损坏或丢失,因为这些文件不是以字符为单位组织的。字符流更适合处理文本文件,它们能够处理不同的字符编码,如UTF-8、ISO-8859-1等。对于二进制文件的复制,应使用字节流。
确保将
sourceFilePath
和destinationFilePath
替换为实际的源文件和目标文件路径。
使用流实现为你文件夹的整体拷贝
import java.io.*;
public class FolderCopyWithByteStreams {
public static void main(String[] args) {
String sourceFolderPath = "path/to/source/folder";
String destinationFolderPath = "path/to/destination/folder";
try {
copyFolder(sourceFolderPath, destinationFolderPath);
System.out.println("文件夹复制成功!");
} catch (IOException e) {
e.printStackTrace();
System.out.println("文件夹复制失败:" + e.getMessage());
}
}
public static void copyFolder(String sourceFolderPath, String destinationFolderPath) throws IOException {
File sourceFolder = new File(sourceFolderPath);
File destinationFolder = new File(destinationFolderPath);
// 如果目标文件夹不存在,则创建它
if (!destinationFolder.exists()) {
destinationFolder.mkdirs();
}
// 遍历源文件夹中的所有文件和子文件夹
for (File file : sourceFolder.listFiles()) {
String sourceFilePath = file.getAbsolutePath();
String destinationFilePath = destinationFolder.getAbsolutePath() + File.separator + file.getName();
if (file.isDirectory()) {
// 如果是子文件夹,则递归调用copyFolder方法
copyFolder(sourceFilePath, destinationFilePath);
} else {
// 如果是文件,则直接拷贝文件
copyFile(sourceFilePath, destinationFilePath);
}
}
}
public static void copyFile(String sourceFilePath, String destinationFilePath) throws IOException {
try (InputStream inputStream = new FileInputStream(sourceFilePath);
OutputStream outputStream = new FileOutputStream(destinationFilePath)) {
byte[] buffer = new byte[4096]; // 使用一个缓冲区来提高复制效率
int bytesRead;
// 读取源文件内容并写入目标文件
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
}
}
}
在这段代码中,我们定义了两个方法:
copyFolder
和copyFile
。
copyFolder
方法负责遍历源文件夹中的所有文件和子文件夹。对于每个子文件夹,它递归地调用自己;对于每个文件,它调用copyFile
方法来拷贝文件。copyFile
方法负责实际的文件拷贝操作。它使用字节流来读取源文件的内容,并写入到目标文件中。这里使用了缓冲区来提高拷贝效率。请注意,
listFiles()
方法可能返回null
,如果文件夹不存在或者由于某种安全原因无法访问。在实际应用中,你应该添加适当的错误处理代码来处理这种情况。另外,请确保替换
sourceFolderPath
和destinationFolderPath
变量的值为实际的源文件夹和目标文件夹的路径。此代码没有处理文件或文件夹的权限问题,也没有实现覆盖目标位置已存在文件的逻辑。在实际应用中,你可能需要根据需要添加这些功能。
常见的涉及到流技术的相关操作需求:(1)数据与文件间的写出和读取(2)文件和文件夹的复制
大体的代码逻辑如下:
(1)建立文件源,比如源文件,目标文件,new File)对象,并且制定相关的路径和文件的名称
(2)建立流对象,例如inputStream、outStream、Reader、Wirter
(3)建立文件源和流对象的关系
(4)创建单位数组,采用循环的思路通过流对象的reader、.wirter的读写方法读取和写出数据
(5)异常的处理,例如文件不存在或者是文件路径有问题,还有就是关闭所有的流资源
常用的API
String
被称为构造字符串对象
常量对象:字符串常量对象是用双引号括起的字符序列,例如:“你好"、"12.97"、"boy"等。
String类较常用构造方法:
String(s),
String (char a[]),
String(char a[],int startlndex,int count)
String类的常用方法:
public boolean startsWith(String s)
public boolean endsWith(String s):判断当前字符串对象的前缀(后缀)是否是参数s指定的字符串
public int compareTo(String s)按字典序与参数s指定的字符串比较大小
public int compareTolgnoreCase(String s)
public boolean contains(String s):判断当前字符串对象是否含有参数指定的字符串s
public int indexOf(String s):从当前字符串的头开始检索字符串s,并返回首次出现s的位置
indexOf(String s ,int startpoint)
lastIndexOf(String s)
public String substring(int startpoint):获得一个当前字符串的子串
substring(int start,int end)
public String trim0:得到一个去掉前后空格后的字符串对象
public boolean equals(Object anObject){
if(this == anObject){
return true;
}
if(anObject instanceof String){
String anotherString = (String)anObject;
int n = value.length;
if(n==anotherString.value.length){
char v1[]=value;
char v2[]=anotherString.value;
int i=0;
while(n-- !=0){
if(v1[i]!=v2[i])
return false;
i++
}
return true'
}
}
return false;
}
字符串与基本数据的相互转化
public static int parselnt(String s):可以将由“数字”字符组成的字符串
类似地,使用java.lang包中的Byte、Short、Long、Float、Double类调相应的类方法可以将由“数字”字符组成的字符串,转化为相应的基本数据类型。
强转:大转小,小转大自动转换
字符串与字符、字节数组
String类的构造方法:String(char)和String(char[],int offset,int length)分别用字符数组中的全部字符和部分字符创建字符串对象
String类提供了将字符串存放到数组中的方法:
public void getChars(int start,int end,char c[],int offset)
将字符串中的全部字符存放在一个字符数组中的方法:
public char[] toCharArray()
String(byte)用指定的字节数组构造一个字符串对象。String(byte[],int offset,int length)用指定的字节数组的一部分,即从数组起始位置offset开始取length个字节构造一
个字符串对象。
public byte[]getBytes()方法使用平台默认的字符编码,将当前字符串转化为一个字节数组。
public byte[]getBytes(String charsetName)使用参数指定字符编码,将当前字符串转化为一个字节数组。
StringBuffer类
String s = new String("我喜欢学习");
StringBuffer buffer = new StringBuffer("我喜欢学习");
buffer.append("数学")
StringBuffer对象的创建
StringBuffer类有三个构造方法
- StringBuffer()
- StringBuffer(int size)
- StringBuffer(String s)
StringBuffer类常用方法
- StringBuffer append(String s)StringBuffer append(int n)
- StringBuffer append(Object o)StringBuffer append(char n)
- StringBuffer append(long n),String Buffer append(boolean n)
- StringBuffer append(float n),StringBuffer append(double n)
- public chat charAt(int n)
- public void setCharAt(int n,char ch)
- StringBuffer insert(int index,String str)
- public StringBuffer reverse()
- StringBuffer delete(int startlndex,int endIndex)
- StringBuffer replace(int startIndex ,int endlndex,String str)
StringBuffer VS StringBuilder
String Builder和StringBuffer非常类似,均代表可变的字符序列,而且方法也一样
String:不可变字符序列
String Builder:可变字符序列、效率高、线程不安全
String Buffer:可变字符序列、效率低、线程安全
String使用陷阱:
String s="a";/创建了一个字符串0s=s+"b";/实际上原来的"a"字符串对象已经丢弃了,现在又产生了一个字符串s+"b"(也就是"b")。如果多次执行这些改变串内容的操作,会导致大量副本字符串对象存留在内存中,降低效率。如果这样的操作放到循环中,会极大影响程序的性能。String Buffer类
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.ms08067.api;
import java.io.PrintStream;
public class Demo01 {
public Demo01() {
}
public static void main(String[] args) {
String str = new String("abcd");
String str2 = new String("abcd");
PrintStream var10000 = System.out;
boolean var10001 = str2.equals(str);
var10000.println("两个string的值是否相等:" + var10001);
System.out.println("两个string变量是否属于同一个对象:");
System.out.println(str2 == str);
var10000 = System.out;
char var14 = str.charAt(2);
var10000.println("返回指定索引处的字符:" + var14);
var10000 = System.out;
int var15 = str.indexOf("c");
var10000.println("获取指定索引的字符:" + var15);
var10000 = System.out;
var10001 = str.endsWith("d");
var10000.println("字符串以某字符串或字符结尾:" + var10001);
var10000 = System.out;
var10001 = str.startsWith("ab");
var10000.println("字符串以某字符串或字符开头:" + var10001);
String str3 = "def";
String str4 = "def";
System.out.println(str3.equals(str4));
System.out.println(str3 == str4);
System.out.println(str3.indexOf(121));
String s = str3.substring(0);
System.out.println("获取子字符串" + s);
String str5 = str3.replace('e', '*');
System.out.println("字符串替换:" + str5);
String str6 = "abcde,rrtt,cccee";
String[] strArray = str6.split(",");
for(int i = 0; i < strArray.length; ++i) {
System.out.println(strArray[i]);
}
String str7 = "aabb";
String str8 = str7.trim();
System.out.println("返回字符串的长度" + str8.length());
System.out.println("Abc".equalsIgnoreCase("abc"));
System.out.println("返回指定字符索引:" + "Abcbd".indexOf(98));
System.out.println("返回字符最后一次出现的位置:" + "Abcbd".lastIndexOf(98));
System.out.println("Abcbd".endsWith("bd"));
System.out.println("Abcbd".toLowerCase());
System.out.println("Abcbd".toUpperCase());
System.out.println("##################");
String gh = new String("a");
for(int i = 0; i < 1000; ++i) {
gh = gh + i;
}
System.out.println(gh);
}
}
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.ms08067.api;
public class Demo02 {
public Demo02() {
}
public static void main(String[] args) {
StringBuilder sb = new StringBuilder("abcdefghijklmnopqrstuvwxyz");
sb.append(true).append("ABC");
System.out.println(sb);
sb.delete(3, 5);
System.out.println(sb);
sb.replace(3, 5, "Hello");
System.out.println(sb);
System.out.println(sb.length());
StringBuffer stringBuffer = new StringBuffer("weer");
stringBuffer.append("fsdfdsf");
System.out.println(stringBuffer);
System.out.println("长度:" + stringBuffer.length());
}
}
作业5
需求:创建一个文本文件,内容为创建的当前日期,文件名称为:当前日期字符串+随机7位数字+txt,例如202107082345.txt
提示:需要应用到得知识点:
1、使用1O创建文本对象并写出数据
2、使用日期类相关AP获取当前时间
3、使用Math类相关方法随机获取4位数字,字符串拼接日期+随机数
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
public class CreateFileWithDateAndRandom {
public static void main(String[] args) {
// 获取当前日期
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
String currentDate = dateFormat.format(new Date());
// 生成随机7位数字
Random random = new Random();
int randomNumber = 1000000 + random.nextInt(9000000); // 保证是7位数字
// 拼接文件名
String fileName = currentDate + randomNumber + ".txt";
// 创建文件并写入当前日期
try (BufferedWriter writer = new BufferedWriter(new FileWriter(fileName))) {
writer.write(currentDate);
} catch (IOException e) {
e.printStackTrace();
}
}
}
多线程
多线程概念
Java给多线程编程提供了内置的支持。一个多线程程序包含两个或多个能并发运行的部分。程序的每一部分都称作为一个线程,并且每个线程定义了一个独立的执行程序
多线程任务的始终特别的形式。多线程比多任务需要更小的开销
进程:一个进程包括由操作系统分配的内存空间,包含一个或多个线程。一个线程不能独立的存在,它必须是进程的一部分。一个进程一直运行,知道所有的非守候线程都结束运行后才能结束
多项成满足程序员编写非常有效率的运行那个程序来达到充分利用CPU的目的,因为CPU的空闲时间能够保持在最低限度
线程生命周期
- 新建:当创建Thread类的一个实例(对象)时,此线程进入新建状态(未被启动)
- 就绪:线程已经被启动,正在等待被分配给CPU时间片,此时线程正在就绪队列中排队等待得到的CPU资源
- 运行:线程获得CPU资源正在执行任务(run()方法)此时除非此线程自动放弃CPU资源或者有有限级更高的线程进入,线程将一直运行结束
- 堵塞(blocked)由于某种原因导致正在运行的线程让出CPU并暂停自己的执行,即进入堵塞状态
- 死亡(dead)当线程执行完毕或被其他线程杀死,线程就进入死亡状态,此时线程不可能再进入就绪状态等待执行
多线程实现方式
通过实现Runnable接口,继承Thread,使用Callable三种方式创建线程
练习一
场景1-默认乌龟与兔子赛跑
Tortoise
public class Tortoise extends Thread{
@Override
public void run(){
//线程体
for(int i=0;i<100;i++){
System.out.println("乌龟跑了"+i+"步");
}
}
}
Rabbit
public class Rabbit extends Thread{
@Override
public void run(){
//线程体
for(int i=0;i<100;i++){
System.out.println("兔子跑了"+i+"步");
}
}
}
测试
package com.ms08067.auditcode.thread.create;
public class RabbitApp{
/**
* @param args
*/
public static void main(String[] args){
//创建子类对象
Rabbit rab = new Rabbit();
Tortoise tor = new Tortoise();
//调用start方法
rab.start();
rab.run();
tor.start();
tor.run();
}
}
场景二-模拟12306抢票
package com.ms08067.thread;
public class Demo02{
public static void main(String[] args){
//创建线程对象
Web12306 web12306 = new Web12306();
//创建三个用户(代理)
Thread thread1 = new Thread();
Thread thread2 = new Thread();
Thread thread3 = new Thread();
//启动三个用户的进程
thread1.start();
thread2.start();
thread3.start();
}
}
class Web12306 implements Runnable{
private int num = 50; //初始化票的余额
@Override
public void run(){
while(true){
if(num<=0){
break; //跳出循环
}
System.out.println(Thread.currentThread().getName()+"抢到了"+num--);
}
}
}
异常处理与网络编程
异常处理
程序运行时,发生的不被期望的事件,它阻止了程序按照程序员的预期正常执行,这就是异常。异常发生时,是任程序自生自灭,立刻退出终止,还是输出错误给用户?或者用C语言风格:用函数返回值作为执行状态?在Java中即,Java在编译或运行或者运行过程中出现作物
异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候是可以避免的
异常分类
检查性异常:最据代表的检查性异常是用户错误或问题引起了的异常,这是程序员无法预见的
运行时异常:运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略
错误:错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略
所有的异常类是从java.lang.Exception类继承的子类
Exception类是Throwable类的子类。除Exception类之外,Throwable还有一个子类Error
Java程序通常不捕获错误。程序一般发生在严重故障时,它们在Java程序处理的范畴之外
Error用来指示运行时环境发生的错误
异常类有两个主要的子类:IOException类和RuntimeException类
java常见异常类
异常处理手段
捕获异常
使用try和catch关键字可以捕获异常。try和catch代码块放在异常可能发生的地方
try和catch代码块中的代码称为保护代码
单重捕获
try
{
//程序代码
}catch(ExceptionName e1)
{
//Catch块
}
多重捕获
try {
//程序代码
}catch(异常类型1 异常的变量名1){
//程序代码
}catch(异常类型2 异常的变量名2){
//程序代码
}
练习一
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.ms08067.exception;
public class Demo01 {
public Demo01() {
}
public static void main(String[] args) {
int[] a = new int[2];
try {
System.out.println("Access element three :" + a[3]);
} catch (ArrayIndexOutOfBoundsException var6) {
System.out.println("Exception thrown :" + var6);
} finally {
a[0] = 6;
System.out.println("First element value: " + a[0]);
System.out.println("The finally statement is executed");
}
}
}
网络编程
网络编程是指编写运行在多个设备(计算机)的程序,这些设备都通过网络连接起来。
java.net包中J2SE的API包含有类和接口,它们提供低层次的通信细节。你可以直接使用这
些类和接口,来专注于解决问题,而不用关注通信细节。
java.net包,中提供了两种常见的网络协议的支持:
TCP:TCP是传输控制协议的缩写,它保障了两个应用程序之间的可靠通信。通常用于互
联网协议,被称TCP/IP。.
UDP:UDP是用户数据报协议的缩写,一个无连接的协议。提供了应用程序之间要发送的
数据的数据包。
Socket编程
套接字使用TCP提供了两台计算机之间的通信机制。客户端程序创建一个套接字,并尝
试连接服务器的套接字。
当连接建立时,服务器会创建一个Socket对象。客户端和服务器现在可以通过对Socket
对象的写入和读取来进行通信。
java.net.Socket类代表一个套接字,并且java.net.ServerSocket类为服务器程序提供了一
种来监听客户端,并与他们建立连接的机制。
以下步骤在两台计算机之间使用套接字建立TCP连接时会出现:
- 服务器实例化一个ServerSockety对象,表示通过服务器上的端口通信。
- 服务器调用ServerSocket类的accept()方法,该方法将一直等待,直到客户端连接到服务器上给定的端口。
- 服务器正在等待时,一个客户端实例化一个Socket对象,指定服务器名称和端口号来请求连接。
- Socket类的构造函数试图将客户端连接到指定的服务器和端口号。如果通信被建立,则在客户端创建一个Socket对象能够与服务器进行通信。
- 在服务器端,accept()方法返回服务器上一个新的socket引用,该socket连接到客户端的socket。
连接建立后,通过使用l/O流在进行通信。每一个socketi都有一个输出流和一个输入流。客户端的输出流连接到服务器端的输入流,而客户端的输入流连接到服务器端的输出流。 - TCP是一个双向的通信协议,因此数据可以通过两个数据流在同一时间发送以下是一些类提供的一套完整的有用的方法来实现sockets。
Socket相关API方法
练习二
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.ms08067.net;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class Demo01 {
public Demo01() {
}
public static void main(String[] args) throws UnknownHostException {
InetAddress addr = InetAddress.getLocalHost();
System.out.println(addr.getHostAddress());
System.out.println(addr.getHostName());
addr = InetAddress.getByName("www.163.com");
System.out.println(addr.getHostAddress());
System.out.println(addr.getHostName());
addr = InetAddress.getByName("139.215.132.55");
System.out.println(addr.getHostAddress());
System.out.println(addr.getHostName());
}
}