该运算符用于操作对象实例,检查该对象是否是一个特定类型(类类型或接口类型)
(Object reference variable) instanceof (class/interface type); Object name = "James"; Object age = 17; boolean resultName = name instanceof String; // 由于 name 是 String 类型,所以返回 true boolean resultAge = age instanceof String; // 由于 age 是 Integer 类型,所以返回 false
其中左边 obj 为一个对象,右边 Class 表示一个类或者一个接口,当 obj 为 Class 的对象,或者是其直接或间接子类,或者是其接口的实现类,结果 result 都返回 true,否则返回false。
注:编译器会检查 obj 是否能转换成右边的 Class 类型,如果不能转换则直接报错,如果不能确定类型,则通过编译,具体看运行时定。
2. Asset 断言用于对值进行判断,为真则运行,否则报错,多用于调试
Assert.isNull(null, "是否为空"); // 为空则运行
Assert.notNull("", "是否不为空"); // 不为空则运行
Assert.notEmpty(new ArrayList<>(), "数组是否不为空"); // 不为空则运行
可以使用 assert 进行更多判断操作,使用原则:
- 使用断言捕捉不应该发生的非法情况。不要混淆非法情况与错误情况之间的区别,后者是必然存在的并且是一定要作出处理的。使用断言对函数的参数进行确认。在编写函数时,要进行反复的考查,并且自问:“我打算做哪些假定?”一旦确定了的假定,就要使用断言对假定进行检查。一般教科书都鼓励程序员们进行防错性的程序设计,但要这种编程风格会隐瞒错误。当进行防错性编程时,如果"不可能发生"的事情的确发生了,则要使用断言进行报警。
新版 IDEA 默认把 VM Options 去掉了,需要手动加进来
在 VM Options 里输入 -ea 开启断言
public static void main(String[] args) {
int num = 10;
// 判断 num 是否大于 12,不大于则为假,报错;大于则为真,继续运行
assert num > 12;
System.out.println("运行");
}
也可以在后面定义错误信息
public static void main(String[] args) {
int i = 10;
// 假如 i < 10,则 i++,否则则报错
assert (i++ < 10) : "num 大于等于 10";
System.out.println(i);
}
3. 泛型
? 表示不确定的 Java类型T (type)表示 Java类型K 代表 Java 键值中的 KeyV 代表 Java 键值中的ValueE 代表 Element 3.1 定义和使用泛型类
[修饰符] class 类名<代表泛型的变量> { }
// 定义泛型类 class User3.2 定义和使用泛型方法{ private Fan type; public User(Fan type) { this.type = type; } @Override public String toString() { return "User{" + "type=" + type + '}'; } public Fan getType() { return type; } public void setType(Fan type) { this.type = type; } } // 使用 public static void main(String[] args) { User stringUser = new User<>("String"); // 可以限制泛型为 String System.out.println(stringUser); // 输出 String User user = new User<>(1); // 不进行限制 System.out.println(user); // 输出 1 user.setType("类型"); System.out.println(user); // 输出 类型 }
修饰符 <代表泛型的变量> 返回值类型 方法名(参数) { }
// 定义泛型方法
class Person{
public void show(Fan fan){
System.out.println(fan);
}
public Fan showFan(Fan fan){
return fan;
}
}
// 使用
public static void main(String[] args) {
Person person = new Person();
person.show("String"); // 无返回,直接输出
// 传入一个 List 的 User泛型对象,User 的泛型又为 List
ArrayList>> users = person.showFan(new ArrayList>>(){
{
this.add(new User<>(new ArrayList(){
{
this.add("array11");
this.add("array12");
}
}));
this.add(new User<>(new ArrayList(){
{
this.add("array21");
this.add("array22");
}
}));
}
});
System.out.println(users); // 输出返回值
}
3.3 定义和使用泛型接口
修饰符 interface 接口名<代表泛型的变量> { }
interface MyInterface3.3.1 定义实现类时确定泛型的类型{ void add(Fan fan); Fan get(Fan fan); }
class MyInterfaceImpl implements MyInterface3.3.2 始终不确定泛型的类型,直到创建对象时,确定泛型的类型{ @Override public void add(String s) { System.out.println(s + ",fan"); } @Override public String get(String s) { return s; } }
// 实现类 class MyInterfaceImpl3.4 泛型通配符 ?implements MyInterface { @Override public void add(Fan fan) { System.out.println(fan + ",fan"); } @Override public Fan get(Fan fan) { return fan; } } // 主启动类 public static void main(String[] args){ MyInterface myInterface = new MyInterfaceImpl(); // 不限制泛型 myInterface.add(1); MyInterface myInterface1 = new MyInterfaceImpl<>(); // 限制泛型为 String System.out.println(myInterface1.get("String")); }
当使用泛型类或者接口时,传递的数据中,泛型类型不确定,可以通过通配符 > 表示。但是一旦使用泛型的通配符后,只能使用 Object 类中的共性方法,集合中元素自身方法无法使用。
3.4.1 通配符基础使用泛型的通配符:不知道使用什么类型来接收的时候,此时可以使用 ?,? 表示未知通配符。
此时只能接受数据,不能往该集合中存储数据。
public static void main(String[] args) {
get(new ArrayList(){
{
this.add(1);
this.add(2);
}
});
get(new linkedList(){
{
this.add("String1");
this.add("String1");
}
});
}
public static void get(Collection> collection){ // 可以传入任意的 Collection 下的类型
collection.stream().forEach(o -> {
System.out.println(o);
});
}
3.4.2 通配符高级使用----受限泛型
之前设置泛型的时候,实际上是可以任意设置的,只要是类就可以设置。但是在JAVA的泛型中可以指定一个泛型的上限和下限。
泛型的上限:
格式: 类型名称 extends 类 > 对象名称意义: 只能接收该类型及其子类
泛型的下限:
格式: 类型名称 super 类 > 对象名称意义: 只能接收该类型及其父类型
// 泛型的上限:此时的泛型?,必须是Number类型或者Number类型的子类
// Collection 可以换为 ArrayList 等其他类型
public static void getElement1(Collection extends Number> collection){
}
// 泛型的下限:此时的泛型?,必须是Number类型或者Number类型的父类
public static void getElement2(Collection super Number> collection){
}
// 现已知 Object 类,String 类,Number 类,Integer 类,其中 Number 是 Integer 的父类
public static void main(String[] args) {
Collection integer = new ArrayList();
Collection string = new ArrayList();
Collection number = new ArrayList();
Collection
4. 反射
框架:半成品软件。可以在框架的基础上进行软件开发,简化编码反射:将类的各个组成部分封装为其他对象,这就是反射机制
好处:
- 可以在程序运行过程中,操作这些对象。可以解耦,提高程序的可扩展性。
- Class.forName(“全类名”):将字节码文件加载进内存,返回Class对象
多用于配置文件,将类名定义在配置文件中。读取文件,加载类类名.class:通过类名的属性 class 获取
多用于参数的传递对象.getClass():getClass()方法是Object类中的方法,Object类是所有类的父类,所有创建的对象都有该方法.
多用于对象的获取字节码的方式
结论:
同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个。
4.2 Class对象功能
获取功能:
- 获取成员变量们
// 获取所有 public 的成员变量,包括继承的 Field[] getFields() // 获取指定名称的 public 成员变量 Field getField(String name) // 获取所有的成员变量,不考虑修饰符,不包括继承的(这里只能获取到 private 的属性,但并不能访问该 private 字段的值,除非加上 setAccessible(true)) Field[] getDeclaredFields() // 获取指定名称的所有成员变量,不考虑修饰符 Field getDeclaredField(String name)获取构造方法们
// 获取所有 public 的构造方法 Constructor>[] getConstructors() // 获取传入数据类型的对应构造方法,Constructor获取成员方法们constructor = userTestClass.getConstructor(String.class, Integer.class); Constructor getConstructor(类>... parameterTypes) // 不考虑修饰符获取 Constructor getDeclaredConstructor(类>... parameterTypes) // 不考虑修饰符获取 Constructor>[] getDeclaredConstructors()
// 获取所有 public 的成员方法,包括继承的
Method[] getMethods()
// 获取指定名称和传入参数类型的 public 成员方法,Method test = userTestClass.getMethod("test", String.class);
Method getMethod(String name, 类>... parameterTypes)
// 获取所有的成员方法,不包括继承的
Method[] getDeclaredMethods()
// 获取指定名称和传入参数类型的所有成员方法
Method getDeclaredMethod(String name, 类>... parameterTypes)
获取全类名String getName()
操作:
- 设置值
void set(Object obj, Object value)获取值
get(Object obj)忽略访问权限修饰符的安全检查(可以获取 private 的成员变量的值)
setAccessible(true):暴力反射
// 示例类
class UserTest{
private String name;
private Integer age;
public String birthday;
public UserTest() {
}
public UserTest(String name, Integer age) {
this.name = name;
this.age = age;
}
public void test(){
System.out.println("test");
}
@Override
public String toString() {
return "UserTest{" +
"name='" + name + ''' +
", age=" + age +
", birthday='" + birthday + ''' +
'}';
}
}
// 使用
public static void main(String[] args) throws Exception {
UserTest userTest = new UserTest(); // 新建一个对象
Class extends UserTest> aClass = userTest.getClass(); // 通过对象.getClass() 的方式获取字节码
Field[] fields = aClass.getFields(); // 获取 public 的成员变量
for (Field field : fields) {
// public java.lang.String fan.demo02.UserTest.birthday
System.out.println(field);
System.out.println(field.getName()); // birthday
System.out.println(field.get(userTest)); // 获取值 null
field.set(userTest,"2001"); // 设置值,传入对象和设置的值
System.out.println(field.get(userTest)); // 2001
}
}
4.4 Constructor 构造方法
创建对象:
T newInstance(Object… initargs)
注:如果使用空参数构造方法创建对象,操作可以简化:Class对象.newInstance方法
// 使用
public static void main(String[] args) throws Exception {
// 无参构造创建对象
Class userTestClass = UserTest.class; // 通过类名.class 的方式获取字节码
UserTest userTest = userTestClass.newInstance(); // 直接通过Class对象.newInstance() 方法创建对象
UserTest userTest = userTestClass.getConstructor().newInstance(); // 获取构造方法再调用 newInstance 方法
// 有参构造创建对象,传入对应的数据类型
Constructor constructor = userTestClass.getConstructor(String.class, Integer.class);
UserTest userTest = constructor.newInstance("张三",1);
}
4.5 Method 方法对象
执行方法:
Object invoke(Object obj, Object… args) :调用 obj 对象的成员方法,参数是args,返回值是 Object 类型
获取方法名称:
String getName:获取方法名
// 使用
public static void main(String[] args) throws Exception {
Class userTestClass = UserTest.class;
Method test = userTestClass.getMethod("test", String.class);
System.out.println(test); // public void fan.demo02.UserTest.test(java.lang.String)
System.out.println(test.getName()); // test
UserTest userTest = userTestClass.newInstance(); // 创建对象
// 调用对象的 test 方法,有传参,传入参数 “李四”,有返回值,输出 “李四”
System.out.println(test.invoke(userTest,"李四"));
// 调用对象的 test1 方法,无传参,无返回值,输出为 null
Method test1 = aClass.getMethod("test1");
System.out.println(test1.invoke(userTest));
}
5. 注解
5.1 概念
注解(Annotation),也叫元数据。一种代码级别的说明。它是 JDK1.5 及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
注解:说明程序的。给计算机看的。@注解名称注释:用文字描述程序的。给程序员看的 5.2 作用分类
- 编写文档:通过代码里标识的注解生成文档【生成文档doc文档】代码分析:通过代码里标识的注解对代码进行分析【使用反射】编译检查:通过代码里标识的注解让编译器能够实现基本的编译 【Override】
@Override :检测被该注解标注的方法是否是继承自父类(接口)的@Deprecated:该注解标注的内容,表示已过时@SuppressWarnings:压制警告
一般传递参数 all @SuppressWarnings(“all”)
5.4 自定义注解
格式:
// 元注解
public @interface 注解名称{
属性列表;
}
本质:
注解本质上就是一个接口,该接口默认继承 Annotation 接口
public interface MyAnnotation extends java.lang.annotation.Annotation { }
属性:接口中的抽象方法
1、属性的返回值类型有下列取值
基本数据类型String枚举注解以上类型的数组 2、定义了属性,在使用时需要给属性赋值
如果定义属性时,使用 default 关键字给属性默认初始化值,则使用注解时,可以不进行属性的赋值如果只有一个属性需要赋值,并且属性的名称是 value,则 value 可以省略,直接定义值即可数组赋值时,值使用 { } 包裹。如果数组中只有一个值,则 { } 可以省略
元注解:用于描述注解的注解
@Target:描述注解能够作用的位置
ElementType取值:
TYPE:可以作用于类上METHOD:可以作用于方法上FIELD:可以作用于成员变量上 @Retention:描述注解被保留的阶段(存活阶段)
@Retention(RetentionPolicy.RUNTIME):当前被描述的注解,会保留到 class 字节码文件中,并被 JVM 读取到 @documented:描述注解是否被抽取到 api 文档中@Inherited:所标记的类的子类也会拥有这个注解
// 自定义一个注解
@Target({ElementType.FIELD, ElementType.TYPE}) // 作用在变量和方法上
@Retention(RetentionPolicy.RUNTIME)
@documented
public @interface MyAnnotation {
String value() default "fan"; // 设置默认值
boolean isNull(); // 不设置默认值,通过标注注解时传入
}
// 配置注解
@MyAnnotation(isNull = true)
@Data
class Person {
private String name;
private int age;
private boolean isDelete;
}
// 使用
public static void main(String[] args) throws Exception{
Person person = new Person();
Class extends Person> personClass = person.getClass();
// 通过反射,获取作用在类上的注解
MyAnnotation annotation = personClass.getAnnotation(MyAnnotation.class);
// @fan.annotation.MyAnnotation(value=fan, isNull=true)
System.out.println(annotation);
person.setName(annotation.value()); // 注入从注解里获取到的值
person.setDelete(annotation.isNull());
System.out.println(person); // Person(name=fan, age=0, isDelete=true)
}
6. 函数式接口
6.1 概念
函数式接口在Java中是指:有且仅有一个抽象方法的接口。
函数式接口,即适用于函数式编程场景的接口。而Java中的函数式编程体现就是Lambda,所以函数式接口就是可以适用于Lambda使用的接口。只有确保接口中有且仅有一个抽象方法,Java中的Lambda才能顺利地进行推导。
6.2 格式备注:“语法糖”是指使用更加方便,但是原理不变的代码语法。例如在遍历集合时使用的 for-each 语法,其实底层的实现原理仍然是迭代器,这便是“语法糖”。从应用层面来讲,Java 中的 Lambda 可以被当做是匿名内部类的“语法糖”,但是二者在原理上是不同的。
只要确保接口中有且仅有一个抽象方法即可
修饰符 interface 接口名称 {
public abstract 返回值类型 方法名称(可选参数信息);
// 其他非抽象方法内容
}
6.3 @FunctionalInterface 注解
与 @Override 注解的作用类似,Java 8 中专门为函数式接口引入了一个新的注解: @FunctionalInterface 。该注解可用于一个接口的定义上:
@FunctionalInterface
public interface MyFunctionalInterface {
void myMethod();
}
一旦使用该注解来定义接口,编译器将会强制检查该接口是否确实有且仅有一个抽象方法,否则将会报错。需要注意的是,即使不使用该注解,只要满足函数式接口的定义,这仍然是一个函数式接口,使用起来都一样。
6.4 自定义函数式接口// 定义一个函数式接口
@FunctionalInterface
public interface MyFunction {
String myMethod(String name); // public abstract 可以省略
default void hidden() { };
}
// 使用
public static void main(String[] args) {
// 最基础写法
test(new MyFunction() {
@Override
public String myMethod(String name) {
System.out.println("myMethod方法执行!");
return name;
}
});
// 使用 Lambda 表达式
test(name -> {
System.out.println("myMethod方法执行!");
return name;
});
// 假如没有其他语句直接返回值,可以省略大括号和 return
test(name -> name);
}
public static void test(MyFunction myFunction){
String name = myFunction.myMethod("fan");
System.out.println(name);
}
6.5 Lambda 的延迟执行
当 flag 为 false 的时候,Lambda将不会执行。从而达到节省性能的效果
public static void main(String[] args) {
test(false, name -> {
System.out.println("myMethod方法执行!");
return name;
});
}
public static void test(Boolean flag, MyFunction myFunction){
if (flag == true){
String s = myFunction.myMethod("fan");
System.out.println(s);
}else {
System.out.println("未执行");
}
}
6.6 Lambda 作为参数和返回值扩展:实际上使用内部类也可以达到同样的效果,只是将代码操作延迟到了另外一个对象当中通过调用方法来完成。而是否调用其所在方法是在条件判断之后才执行的。
如果抛开实现原理不说,Java 中的 Lambda 表达式可以被当作是匿名内部类的替代品。如果方法的参数是一个函数式接口类型,那么就可以使用 Lambda 表达式进行替代。使用 Lambda 表达式作为方法参数,其实就是使用函数式接口作为方法参数。
6.6.1 作为参数例如 java.lang.Runnable 接口就是一个函数式接口,假设有一个 startThread 方法使用该接口作为参数,那么就可以使用 Lambda 进行传参。这种情况其实和 Thread 类的构造方法参数为 Runnable 没有本质区别。
public class Demo04Runnable {
public static void main(String[] args) {
startThread(() ‐> System.out.println("线程任务执行!"));
}
private static void startThread(Runnable runnable) {
new Thread(runnable).start();
}
}
6.6.2 作为返回值
类似地,如果一个方法的返回值类型是一个函数式接口,那么就可以直接返回一个 Lambda 表达式。当需要通过一个方法来获取一个 java.util.Comparator 接口类型的对象作为排序器时,就可以调该方法获取。
public class Demo06Comparator {
public static void main(String[] args) {
String[] array = { "abc", "ab", "abcd" };
System.out.println(Arrays.toString(array));
Arrays.sort(array, newComparator());
System.out.println(Arrays.toString(array));
}
private static Comparator newComparator() {
return (a, b) ‐> b.length() ‐ a.length();
}
}
6.7 常用函数式接口
JDK 提供了大量常用的函数式接口以丰富Lambda的典型使用场景,它们主要在 java.util.function 包中被提供。
6.7.1 Supplierjava.util.function.Supplier 接口仅包含一个无参的方法: T get() 。用来获取一个泛型参数指定类型的对象数据。由于这是一个函数式接口,这也就意味着对应的 Lambda 表达式需要“对外提供”一个符合泛型类型的对象数据。
public static void main(String[] args) {
System.out.println(test(new Supplier() {
@Override
public String get() { // 参数类型为使用 Supplier 时指定的数据类型
return "fan";
}
}));
System.out.println(test(() -> "fan"));
}
public static String test(Supplier supplier){
return supplier.get();
}
6.7.2 Consumer
java.util.function.Consumer
- 抽象方法:accept
Consumer 接口中包含抽象方法 void accept(T t) ,意为消费一个指定泛型的数据。
public static void main(String[] args) {
test("fan", new Consumer() {
@Override
public void accept(String name) {
System.out.println(name);
}
});
test("fan", name -> System.out.println(name));
}
public static void test(String name, Consumer consumer){
consumer.accept(name);
}
默认方法:andThen如果一个方法的参数和返回值全都是 Consumer 类型,那么就可以实现效果:消费数据的时候,首先做一个操作,然后再做一个操作,实现组合。而这个方法就是 Consumer 接口中的 default 方法 andThen 。
public static void main(String[] args) {
test("fan", one -> System.out.println(one.toLowerCase()),
two -> System.out.println(two.toUpperCase()));
}
public static void test(String name, Consumer one, Consumer two){
Consumer stringConsumer = one.andThen(two);
stringConsumer.accept(name);
one.andThen(two).accept(name);
}
有时候我们需要对某种类型的数据进行判断,从而得到一个 boolean 值结果。这时可以使用 java.util.function.Predicate
- 抽象方法:test
Predicate 接口中包含一个抽象方法: boolean test(T t) 。用于条件判断的场景
public static void main(String[] args) {
test("false", new Predicate() {
@Override
public boolean test(String s) {
return s.length() > 4; // 返回一个判断
}
});
// 判断字符串长度是否小于 4
test("false", s -> s.length() < 4);
}
public static void test(String str, Predicate predicate){
boolean test = predicate.test(str);
System.out.println(test);
}
默认方法:and既然是条件判断,就会存在与、或、非三种常见的逻辑关系。其中将两个 Predicate 条件使用“与”逻辑连接起来实现“并且”的效果时,可以使用 default 方法 and 。
public static void main(String[] args) {
// 判断字符串是否存在 H 并且存在 W
test("fanHW", one -> one.contains("H"), two -> two.contains("W"));
}
public static void test(String str, Predicate one, Predicate two){
boolean test = one.and(two).test(str);
System.out.println(test);
}
默认方法:or与 and 的“与”类似,默认方法 or 实现逻辑关系中的“或”。
public static void main(String[] args) {
// 判断字符串是否存在 H 或存在 W
test("fanH", one -> one.contains("H"), two -> two.contains("W"));
}
public static void test(String str, Predicate one, Predicate two){
boolean test = one.or(two).test(str);
System.out.println(test);
}
默认方法:negate它是执行了test方法之后,对结果 boolean 值进行 “!” 取反。一定要在 test 方法调用之前调用 negate 方法,正如 and 和 or 方法一样
public static void main(String[] args) {
test("fanH", one -> one.contains("H"), two -> two.contains("W"));
}
public static void test(String str, Predicate one, Predicate two){
boolean test = one.or(two).negate().test(str); // 对结果取反
System.out.println(test);
}
java.util.function.Function
- 抽象方法:apply
Function 接口中最主要的抽象方法为:R apply(T t) ,根据类型 T 的参数获取类型R的结果。
// 将 String 类型转换为 Integer 类型
public static void main(String[] args) {
test("1234", new Function() {
@Override
public Integer apply(String s) {
System.out.println("apply方法执行!");
return Integer.parseInt(s); // 返回转换后的类型
}
});
test("1234", s -> Integer.parseInt(s));
}
public static void test(String str, Function function){
Integer apply = function.apply(str);
System.out.println(apply);
}
默认方法:andThenFunction 接口中有一个默认的 andThen 方法,用来进行组合操作,用于“先做什么,再做什么”的场景。
public static void main(String[] args) {
// 先进行类型转换,然后再将转换后的类型的值乘以 10
test("1234", one -> Integer.parseInt(one), two -> two *= 10);
}
public static void test(String str, Function one, Function two){
Integer apply = one.andThen(two).apply(str);
System.out.println(apply);
}
网络编程,就是在一定的协议下,实现两台计算机的通信的程序
7.1 网络通信协议网络通信协议:通信协议是对计算机必须遵守的规则,只有遵守这些规则,计算机之间才能进行通信。这就好比在道路中行驶的汽车一定要遵守交通规则一样,协议中对数据的传输格式、传输速率、传输步骤等做了统一规定,通信双方必须同时遵守,最终完成数据交换。TCP/IP协议:传输控制协议/因特网互联协议( Transmission Control Protocol/Internet Protocol),是Internet最基本、最广泛的协议。它定义了计算机如何连入因特网,以及数据如何在它们之间传输的标准。它的内部包含一系列的用于处理数据通信的协议,并采用分层模型,每一层都呼叫它的下一层所提供的协议来完成自己的需求。模型各分层作用及对应协议 7.2 协议分类
通信的协议还是比较复杂的, java.net 包中包含的类和接口,它们提供低层次的通信细节。我们可以直接使用这些类和接口,来专注于网络程序开发,而不用考虑通信的细节。java.net 包中提供了两种常见的网络协议的支持。
7.2.1 TCP传输控制协议 (Transmission Control Protocol)。TCP协议是面向连接的通信协议,即传输数据之前,在发送端和接收端建立逻辑连接,然后再传输数据,它提供了两台计算机之间可靠无差错的数据传输。
三次握手:TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠。
第一次握手,客户端向服务器端发出连接请求,等待服务器确认第二次握手,服务器端向客户端回送一个响应,通知客户端收到了连接请求第三次握手,客户端再次向服务器端发送确认信息,确认连接
完成三次握手,连接建立后,客户端和服务器就可以开始进行数据传输了。由于这种面向连接的特性,TCP协议可以保证传输数据的安全,所以应用十分广泛,例如下载文件、浏览网页等。
用户数据报协议(User Datagram Protocol)。UDP协议是一个面向无连接的协议。传输数据时,不需要建立连接,不管对方端服务是否启动,直接将数据、数据源和目的地都封装在数据包中,直接发送。每个数据包的大小限制在 64k 以内。它是不可靠协议,因为无连接,所以传输速度快,但是容易丢失数据。日常应用中,例如视频会议、QQ聊天等。
7.3 网络编程三要素 7.3.1 协议计算机网络通信必须遵守的规则
7.3.2 IP地址指互联网协议地址(Internet Protocol Address),俗称 IP。IP 地址用来给一个网络中的计算机设备做唯一的编号。假如我们把“个人电脑”比作“一台电话”的话,那么“IP地址”就相当于“电话号码”。
IP地址分类
IPv4:是一个 32 位的二进制数,通常被分为 4 个字节,表示成 a.b.c.d 的形式,例如 192.168.65.100 。其中 a、b、c、d 都是0~255之间的十进制整数,那么最多可以表示42亿个。IPv6:由于互联网的蓬勃发展,IP地址的需求量愈来愈大,但是网络地址资源有限,使得 IP 的分配越发紧张。有资料显示,全球 IPv4 地址在2011年2月分配完毕。为了扩大地址空间,拟通过 IPv6 重新定义地址空间,采用 128 位地址长度,每 16 个字节一组,分成 8 组十六进制数,表示成ABCD:EF01:2345:6789:ABCD:EF01:2345:6789 ,号称可以为全世界的每一粒沙子编上一个网址,这样就解决了网络地址资源数量不够的问题。
常用命令
查看本机IP地址
ipconfig检查网络是否连通
ping IP地址7.3.3 端口号
网络的通信,本质上是两个进程(应用程序)的通信。每台计算机都有很多的进程,那么在网络通信时,如何区分这些进程呢?
如果说IP地址可以唯一标识网络中的设备,那么端口号就可以唯一标识设备中的进程(应用程序)了。
端口号:用两个字节表示的整数,它的取值范围是0-65535。其中,0~1023之间的端口号用于一些知名的网络服务和应用,普通的应用程序需要使用1024以上的端口号。如果端口号被另外一个服务或应用所占用,会导致当前程序启动失败。
利用 协议 + IP地址 + 端口号 三元组合,就可以标识网络中的进程了,那么进程间的通信就可以利用这个标识与其它进程进行交互。



