Java -- 初级开发者不经常宠幸的重点

【导入】

  • 本篇讲解的一些是作为新手入职之后不经常用到的,但是可能会面试被问到,因此需要理解,以后用到的时候在深入研究。但是不作为现阶段研究的重点,切勿跑偏方向;
  • 到目前为止,java基础的学习就到这个地方,没有写的很全面,还需要后期的补充。有蒙圈的哥们,可以自行复习,接下来就是一点点的web前端知识,还有Maven的研究了。
  • 一起加油!!!ヾ(◍°∇°◍)ノ゙

在这里插入图片描述

【目录】

一、注解

1、概述
  1. 注解很厉害,它可以增强我们的java代码,同时利用反射技术可以扩充实现很多功能。它们被广泛应用于三大框架底层。传统我们通过xml文本文件声明方式,而现在最主流的开发都是基于注解方式,代码量少,框架可以根据注解去自动生成很多代码,从而减少代码量,程序更易读。例如最火爆的SpringBoot就完全基于注解技术实现;
  2. 注解设计非常精巧,初学时觉得很另类甚至多余,甚至垃圾。有了java代码干嘛还要有@注解呢?但熟练之后你会赞叹,它竟然可以超越java代码的功能,让java代码瞬间变得强大。大家慢慢体会吧;
  3. 常见的元注解:@Target、@Retention,jdk提供将来描述我们自定义的注解的注解。听起来好绕,别着急,做两个例子,立刻清晰。现在现有“元注解”这个概念。

注解分类

  • JDK自带注解
  • 元注解
  • 自定义注解
2、JDK注解

JDK注解的注解,就5个:

  • @Override
  • @Deprecated标记就表明这个方法已经过时了,但我就要用,别提示我过期
  • @SuppressWarnings(“deprecation”) 忽略警告
  • @SafeVarargs jdk1.7出现,堆污染,不常用
  • @FunctionallInterface jdk1.8出现,配合函数式编程拉姆达表达式,不常用

常用的@Override ,标志着该方法是一个重写方法

3、元注解

描述注解的注解,就5个:

  • @Target 注解用在哪里:类上、方法上、属性上;值都被维护在ElementType类中
  • @Retention 注解的生命周期:源文件中、class文件中、运行中;值都被维护在RetentionPolicy类中;
  • @Inherited 允许子注解继承
  • @Documented 生成javadoc时会包含注解,不常用
  • @Repeatable注解为可重复类型注解,可以在同一个地方多次使用,不常用
  1. 常用的@Target@Retention
  2. 如果觉得这十个还不够用,还可以自定义注解
1.3.1 @Target ElementType.class

描述注解的使用范围:

  • ElementType.ANNOTATION_TYPE ------> 应用于注释类型
  • ElementType.CONSTRUCTOR ------> 应用于构造函数
  • ElementType.FIELD ------> 应用于字段或属性
  • ElementType.LOCAL_VARIABLE ------> 应用于局部变量
  • ElementType.METHOD ------> 应用于方法级
  • ElementType.PACKAGE ------> 应用于包声明
  • ElementType.PARAMETER ------> 应用于方法的参数
  • ElementType.TYPE ------> 应用于类的元素
1.3.2 @Retention RetentionPolicy.class
  • 定义了该注解被保留的时间长短,某些注解仅出现在源代码中,而被编译器丢弃;
  • 而另一些却被编译在class文件中; 编译在class文件中的注解可能会被虚拟机忽略,而另一些在class被装载时将被读取。
  • 为何要分有没有呢?没有时,反射就拿不到,从而就无法去识别处理。
  • SOURCE ------> 在源文件中有效(即源文件保留)
  • CLASS ------> 在class文件中有效(即class保留)
  • RUNTIME ------> 在运行时有效(即运行时保留)
4、自定义注解
package cn.tedu.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

//这个类用来测试自定义注解
public class Test_Annotation {
	public static void main(String[] args) {
		
	}
}

//1、创建自定义注解:语法:@interface 注解名
//2、@Target指定注解位置 -- ElementType.TYPE
//3、@Retention指定注解的生命周期 -- RetentionPolicy.SOURCE
//@Target(ElementType.TYPE)//2、指定注解的位置
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
@interface Test{
	//5、给注解添加属性 -- 注解的语法和java略有不同
//	String name();
	String name() default "";
	String value()default "";//8、特殊属性赋值时可以简写
	
}
//4、使用自定义的注解Test
//@Test(name = "Daniel")//6、使用Test注解时,同时给name属性赋值
//7、如果name属性有了默认值,我们使用就可以省略赋值
//@Test//要是想修改,可以继续赋值//@Test(name = "Daniel")
@Test("hello")//8.1如果给value属性赋值,可以简写
class hello{
//	@Test  The annotation @Test is disallowed for this location
	String name;
	@Test
	public void method() {
		System.out.println(123);
	}
}

二、反射

1、概述
  • Reflection(反射) 是 Java 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查,或者说“自审”,也有称作“自省”。反射非常强大,它甚至能直接操作程序的私有属性。我们前面学习都有一个概念,private的只能类内部访问,外部是不行的,但这个规定被反射赤裸裸的打破了;
  • 反射就像一面镜子,它可以在运行时获取一个类的所有信息,可以获取到任何定义的信息(包括成员变量,成员方法,构造器等),并且可以操纵类的字段、方法、构造器等部分。
2、为什么需要反射
  • 好好的我们new User(); 不是很好,为什么要去通过反射创建对象呢?
  • 那我要问你个问题了,你为什么要去餐馆吃饭呢?
  • 例如:我们要吃个牛排大餐,如果我们自己创建,就什么都得管理。
  • 好处是,每一步做什么我都很清晰,坏处是什么都得自己实现,那不是累死了。牛接生你管,吃什么你管,屠宰你管,运输你管,冷藏你管,烹饪你管,上桌你管。就拿做菜来说,你能有特级厨师做的好?
  • 那怎么办呢?有句话说的好,专业的事情交给专业的人做,饲养交给农场主,屠宰交给刽子手,烹饪交给特级厨师。那我们干嘛呢?
  • 我们翘起二郎腿直接拿过来吃就好了。
  • 再者,饭店把东西做好,不能扔到地上,我们去捡着吃吧,那不是都成原始人了。那怎么办呢?很简单,把做好的东西放在一个容器中吧,如把牛排放在盘子里。
  • 在开发的世界里,spring就是专业的组织,它来帮我们创建对象,管理对象。我们不在new对象,而直接从spring提供的容器中beans获取即可。Beans底层其实就是一个Map<String,Object>,最终通过getBean(“user”)来获取。而这其中最核心的实现就是利用反射技术。
  • 总结一句,类不是你创建的,是你同事或者直接是第三方公司,此时你要或得这个类的底层功能调用,就需要反射技术实现。有点抽象,别着急,我们做个案例,你就立马清晰。
3、反射Class对象

反射对象有三种方式

  1. Class.forName(“类的全路径”);
  2. 类名.class
  3. 对象.getClass();

测试案例:

package cn.tedu.reflection;

//这个类用来测试 -- 反射
public class Test_Reflection {
	public static void main(String[] args) throws ClassNotFoundException {
		//获取class对象
		Class class1 = Class.forName("cn.tedu.reflection.Test_Reflection");//参数是 类的全路径 = 包名.类名
		
		Class class2 = Test_Reflection.class;
		
//		Test_Reflection t = new Test_Reflection();
		new Test_Reflection().getClass();//匿名内部类
		
		System.out.println(class1);
		System.out.println(class2);
		System.out.println();
		
	}
}

复制包名全路径的方法:鼠标放在类名处,右键选择Copy Qualified Name,即可复制全路径

4、常用方法
  • 获得包名、类名
clazz.getPackage().getName()//包名
clazz.getSimpleName()//类名
clazz.getName()//完整类名
  • !!成员变量定义信息
getFields()//获得所有公开的成员变量,包括继承的变量
getDeclaredFields()//获得本类定义的成员变量,包括私有,不包括继承的变量
getField(变量名)
getDeclaredField(变量名)
  • !!构造方法定义信息
getConstructor(参数类型列表)//获得公开的构造方法
getConstructors()//获得所有公开的构造方法
getDeclaredConstructors()//获得所有构造方法,包括私有
getDeclaredConstructor(int.class, String.class)
  • 方法定义信息
getMethods()//获得所有可见的方法,包括继承的方法
getMethod(方法名,参数类型列表)
getDeclaredMethods()//获得本类定义的方法,包括私有,不包括继承的方法
getDeclaredMethod(方法名, int.class, String.class)
  • 反射新建实例
c.newInstance();//执行无参构造
c.newInstance(6, "abc");//执行有参构造
c.getConstructor(int.class, String.class); //执行含参构造,获取构造方法
  • 反射调用成员变量
c.getDeclaredField(变量名); //获取变量
c.setAccessible(true); //使私有成员允许访问
f.set(实例,); //为指定实例的变量赋值,静态变量,第一参数给 null
f.get(实例); //访问指定实例的变量的值,静态变量,第一参数给 null
  • 反射调用成员方法
获取方法
Method m = c.getDeclaredMethod(方法名, 参数类型列表);
m.setAccessible(true) ;//使私有方法允许被调用
m.invoke(实例, 参数数据) ;//让指定的实例来执行该方法
5、反射的应用
5.1 获取类对象
//Junit单元测试方法:@Test + void + 没有参数
	//运行:必须选中方法名,右键,run as,Junit test...
	//反射Class对象
	@Test
	public void showClass() throws ClassNotFoundException {
		Class class1 = Student.class;
		Class class2 = Class.forName("cn.tedu.reflection.Student");
		Student student = new Student();
		Class class3 = student.getClass();
		
		System.out.println(class1);
		System.out.println(class2);
		System.out.println(class3);
		System.out.println();
	}
5.2 获取构造方法
//获取学生类中的构造方法
	@Test
	public void showConstructor() {
		//1、获取class对象
		Class<?> class1 = Student.class;
		//2、调用方法
		Constructor<?>[] cs = class1.getConstructors();
		//3、遍历数组
		for (Constructor<?> constructor: cs) {
			//4、获取方法名
			String name = constructor.getName();
			//5、获取参数类型
			Class<?>[] cls = constructor.getParameterTypes();
			System.out.println(Arrays.toString(cls));
		}
		System.out.println("******************");
	}
5.3 获取成员方法
//获取学生类中的成员方法
	@Test
	public void showMethod() {
		//1、获取class对象
		Class<?> class1 = Student.class;
		//2、调用方法
		Method[] ms = class1.getMethods();
		//3、遍历数组
		for (Method method : ms) {
			//4、获取方法名
			String name = method.getName();
			System.out.println(name);
			//5、获取方法有没有参数
			Class<?>[] cls = method.getParameterTypes();
			System.out.println(Arrays.toString(cls));
 		}
		System.out.println("=======");
	}
5.4 获取成员变量
//获取学生类中的成员变量
	@Test
	public void showFileds() {
		//1、获取class对象
		Class<?> class1 = Student.class;
		//2、获取所有 公共的 属性
		Field[] fs = class1.getFields();
		//3、遍历数组
		for (Field f : fs) {
			//4、获取变量名
			String name = f.getName();
			System.out.println(name);
			
			//5、获取类型
			String type = f.getType().getName();
			System.out.println(type);
			System.out.println("++++++++++");
		} 
	}
5.5 创建对象
//利用反射创建对象
	@Test
	public void showObject() throws Exception {
		//1、获取class对象
		Class<?> class1 = Student.class;
		//2、利用反射好,让反射创建对象
		Object obj = class1.newInstance();//触发了无参构造
		System.out.println(obj);//cn.tedu.reflection.Student@b7dd107
		
		//含参构造的触发
		//指定你想要触发哪个含参构造
		class1.getConstructor(String.class);//触发string类型的含参构造
		class1.getConstructor(int.class);//触发int类型的含参构造
		Constructor<?> c = class1.getConstructor(String.class,int.class);
		Object obj2 = c.newInstance("兔八哥",20);
		//Student [name=兔八哥, age=20]
		System.out.println(obj2);
		
		System.out.println("--------------");
	}
	
  • 附带测试总代码:

1、先创建一个Student类

package cn.tedu.reflection;

public class Student {
	
	public Student() {}
	public Student(String name) {
		this.name = name;
	}
	public Student(int age) {
		this.age = age;
	}
	public Student(String name,int age) {
		this.name = name;
		this.age = age;
	}
	
	public String name;
	public int age;
	
	public void save() {
		System.out.println("save()......");
	}
	public void show(int num) {
		System.out.println("show()..."+num);
	}
	//重写toString():为了方便查看对象的属性值而不是地址值
	@Override
	public String toString() {
		return "Student [name=" + name + ", age=" + age + "]";
	}
}

2、在创建一个测试类 ---- Junit单元测试

package cn.tedu.reflection;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;

import org.junit.Test;

//这个类用来测试,反射获取学生类里的所有数据
public class Test_ReflectionStudent {
//	public static void main(String[] args) {
//		
//	}
	
	//利用反射创建对象
	@Test
	public void showObject() throws Exception {
		//1、获取class对象
		Class<?> class1 = Student.class;
		//2、利用反射好,让反射创建对象
		Object obj = class1.newInstance();//触发了无参构造
		System.out.println(obj);//cn.tedu.reflection.Student@b7dd107
		
		//含参构造的触发
		//指定你想要触发哪个含参构造
		class1.getConstructor(String.class);//触发string类型的含参构造
		class1.getConstructor(int.class);//触发int类型的含参构造
		Constructor<?> c = class1.getConstructor(String.class,int.class);
		Object obj2 = c.newInstance("兔八哥",20);
		//Student [name=兔八哥, age=20]
		System.out.println(obj2);
		
		System.out.println("--------------");
	}
	
	//获取学生类中的成员变量
	@Test
	public void showFileds() {
		//1、获取class对象
		Class<?> class1 = Student.class;
		//2、获取所有 公共的 属性
		Field[] fs = class1.getFields();
		//3、遍历数组
		for (Field f : fs) {
			//4、获取变量名
			String name = f.getName();
			System.out.println(name);
			
			//5、获取类型
			String type = f.getType().getName();
			System.out.println(type);
			System.out.println("++++++++++");
		} 
	}
	
	//获取学生类中的构造方法
	@Test
	public void showConstructor() {
		//1、获取class对象
		Class<?> class1 = Student.class;
		//2、调用方法
		Constructor<?>[] cs = class1.getConstructors();
		//3、遍历数组
		for (Constructor<?> constructor: cs) {
			//4、获取方法名
			String name = constructor.getName();
			//5、获取参数类型
			Class<?>[] cls = constructor.getParameterTypes();
			System.out.println(Arrays.toString(cls));
		}
		System.out.println("******************");
	}

	//Junit单元测试方法:@Test + void + 没有参数
	//运行:必须选中方法名,右键,run as,Junit test...
	//反射Class对象
	@Test
	public void showClass() throws ClassNotFoundException {
		Class class1 = Student.class;
		Class class2 = Class.forName("cn.tedu.reflection.Student");
		Student student = new Student();
		Class class3 = student.getClass();
		
		System.out.println(class1);
		System.out.println(class2);
		System.out.println(class3);
		System.out.println();
	}
	//获取学生类中的成员方法
	@Test
	public void showMethod() {
		//1、获取class对象
		Class<?> class1 = Student.class;
		//2、调用方法
		Method[] ms = class1.getMethods();
		//3、遍历数组
		for (Method method : ms) {
			//4、获取方法名
			String name = method.getName();
			System.out.println(name);
			//5、获取方法有没有参数
			Class<?>[] cls = method.getParameterTypes();
			System.out.println(Arrays.toString(cls));
 		}
		System.out.println("=======");
	}
}

3、控制台显示:

class cn.tedu.reflection.Student
class cn.tedu.reflection.Student
class cn.tedu.reflection.Student

name
java.lang.String
++++++++++
age
int
++++++++++
[class java.lang.String, int]
[int]
[class java.lang.String]
[]
******************
toString
[]
save
[]
show
[int]
wait
[]
wait
[long, int]
wait
[long]
equals
[class java.lang.Object]
hashCode
[]
getClass
[]
notify
[]
notifyAll
[]
=======
Student [name=null, age=0]
Student [name=兔八哥, age=20]
--------------

6、暴力反射

指可以将程序中的私有的属性或者方法通过反射技术,暴力的获取到资源。需要使用的常见方法如下:
在这里插入图片描述

  • 测试案例

1、创建Person类

package cn.tedu.reflection;

//这个类用来测试暴力反射
public class Person {
	private String name = "jack";
	private int age = 10;
	
	private void show() {
		System.out.println("show()...");
	}
	private void test(int a) {
		System.out.println("test()..."+a);
	}
}

2、测试暴力反射

package cn.tedu.reflection;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;

import org.junit.Test;

//这个类用来测试暴力反射person
public class Test_ReflectionPerson {
	//暴力反射成员方法
	@Test
	public void showMethod() throws Exception {
		//1、获取class对象
		Class<?> class1 = Class.forName("cn.tedu.reflection.Person");
		//2、获取所有的方法们
//		class1.getMethod();// 反射 公共的 资源
		Method[] ms = class1.getDeclaredMethods();//暴力反射
		//getDeclaredMethods();//暴力反射 -- 可以获取公共的或者私有的方法
		//3、循环遍历数组
		for (Method m : ms) {
			String name = m.getName();
			System.out.println(name);
			
			Class<?>[] cls = m.getParameterTypes();
			System.out.println(Arrays.toString(cls));
		}
		
		//4、获取  单个  方法 -- 私有
		//getDeclaredMethod(m,n); m是想要执行的方法名,n是方法需要的参数类型
		Method method = class1.getDeclaredMethod("show",null);
		//5、如何执行show()
		//invoke(m, n);//m是想让哪个对象执行方法,n是方法需要的参数
		
		//!!设置私有可见
		method.setAccessible(true);//必须有这句代码
		Object obj = class1.newInstance();
		method.invoke(obj, null);
	}
	
	//暴力反射成员变量
	public void showFields() throws Exception {
		//1、获取Class对象
		Class<?> class1 = Class.forName("cn.tedu.reflection.Preson");
		
		//2、暴力获取  变量
		Field[] fs = class1.getDeclaredFields();
		
		//获取变量类型
		Field f = class1.getDeclaredField("name");//获取一个,传入属性名
		String type = f.getType().getName();//获取变量类型
		System.out.println(type);
		
		//设置私有可见
		f.setAccessible(true);
		Object obj = class1.newInstance();
		
		//设置私有属性的值
		//set(m, n); -- m是要是指哪个对象名,n是要设置的值
		f.set(obj, "大头");
		
		//获取属性的值
		Object value = f.get(obj);//参数是要获取哪个对象的name属性的值
		System.out.println(value);//属性的值
	}
}

3、控制台显示

test
[int]
show
[]
show()...

三、内部类

1、概述
  • 如果一个类存在的意义就是为指定的另一个类,可以把这个类放入另一个类的内部。就是把类定义在类的内部的情况就可以形成内部类的形式。
  • A类中又定义了B类,B类就是内部类。B类可以当做A类的一个成员看待。
    格式:
class A{//外部类
	class B{//内部类:可以看做是外部类的成员
		//变量+方法
	}
}

位置不同,内部类的名字和作用就不同。
如果是在成员位置(类里方法外)- 成员内部类 ---- 用!!
如果是在局部位置(方法里)-局部内部类 ---- 不用!!
匿名内部类 – 最常用!!

2、特点
  • 内部类可以直接访问外部类中的成员,包括私有成员;
  • 外部类要访问内部类的成员,必须要建立内部类的对象;
  • 在成员位置的内部类是成员内部类;
  • 在局部位置的内部类是局部内部类;
3、成员内部类:在成员位置

1、定义内部类

package cn.tedu.innerclass;

//这个类用来定义内部类
public class InnerClass {
//	int age;
//	private int age = 10;
	static private int age = 10;
	
	public void save() {
		System.out.println("Innerclass.save()");
//		get();//未定义
		//特点2:外部类想要使用内部类成员 -- 必须创建内部类对象
		Inner in2 = new Inner();
		in2.get();
		System.out.println(in2.name);
	}
	
	//成员内部类
//	private class Inner{
//		String name = "大头";
//		public void get() {
//			//特点1:内部类可以使用所有外部类成员
			save();
//			System.out.println(age);
//			System.out.println("惊雷!!");
//		}
//	}
	static class Inner{
		static int sum = 30;
		String name = "大头";
		public void get() {
			//特点1:内部类可以使用所有外部类成员
//			save();
			System.out.println(age);
			System.out.println("惊雷!!");
		}
	}
}

2、测试内部类

package cn.tedu.innerclass;

//这个类用来测试内部类
public class Test_InnerClass {
	public static void main(String[] args) {
		//1、想办法 使用 内部类的资源 -- 创建内部类对象
		//外部类.内部类 变量名 = 外部类对象.内部类对象
//		InnerClass.Inner in = new InnerClass().new Inner();
//		in.get();
//		System.out.println(in.name);
		
		//2、当内部类被private修饰后,我们无法直接访问内部类资源
		//需要访问外部类的资源来间接实现内部类的资源
		InnerClass in = new InnerClass();
		in.save();
		System.out.println();
		
		//3、当内部类被静态static修饰后,可以直接被类名调用
		//创建内部类对象
//		InnerClass.Inner in2 = new InnerClass().new Inner();
		InnerClass.Inner in2 = new InnerClass.Inner();
		in2.get();
		System.out.println(in2.name);
		
		//4、静态的内部类里的静态资源 -- 链式编程
		System.out.println(InnerClass.Inner.sum);
	}
}

3.1 被private修饰
	private class Inner{
		String name = "大头";
		public void get() {
			//特点1:内部类可以使用所有外部类成员
//			save();
			System.out.println(age);
			System.out.println("惊雷!!");
		}
	}
3.2 被static修饰
static class Inner{
		static int sum = 30;
		String name = "大头";
		public void get() {
			//特点1:内部类可以使用所有外部类成员
//			save();
			System.out.println(age);
			System.out.println("惊雷!!");
		}
	}
4、匿名内部类
package cn.tedu.innerclass;

//这个类用来测试 -- 匿名内部类
public class Test2_InnerClass2 {
	public static void main(String[] args) {
		new Xin();//匿名对象
		
		//2、接口可以创建对象吗? -- 不可以
		//如果同时使用了匿名对象和匿名内部类是可以直接new的,就相当于创建了接口的实现类
		//匿名的好处是:用起来方便。坏处是:一次只执行一个任务
		new Inter1() {
			@Override
			public void save() {
				System.out.println("save()");
			}
			@Override
			public void get() {
				System.out.println("get()");
			}
		}.get();//3、触发方法的执行
		
		//5、抽象类直接new可以吗? -- 不可以
		new AbstractDemo() {
			@Override
			public void sleep() {
				System.out.println("sleep()...");
			}
		}.sleep();//6、触发方法
	}
}

//4、定义抽象类
abstract class AbstractDemo{
	public void eat() {
		System.out.println("eat()...");
	}
	abstract public void sleep();
}

//1、定义接口
interface Inter1{
	void save();
	void get();
}
class Xin{
	
}

控制台显示:

get()
sleep()...

四、Socket编程

1、概述
  • 也叫套接字编程,是一个抽象层。
  • 应用程序可以通过它发送或接收数据,可对其像对文件一样的打开、读写和关闭等操作。套接字允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信。网络套接字是IP地址与端口与协议的组合。
  • Socket就是为网络编程提供的一种机制 / 通信的两端都有Socket
  • 网络通信其实就是Socket间的通信 / 数据在两个Socket间通过IO传输

在这里插入图片描述

2、服务器端
  • 在服务器端,选择一个端口号,在指定端口上等待客户端发起连接。
  1. 创建对象
    ServerSocket(int port)
  2. 常用方法
    Socket accept()
  3. 侦听并接受到此套接字的连接。
    void close()
  4. 关闭此套接字。
ServerSocket ss = new ServerSocket(9000);//启动服务
Socket socket = ss.accept();//等待客户端发起连接,并建立连接通道
3、客户端
  1. 创建对象
    Socket(String host, int port)
    创建一个流套接字并将其连接到指定主机上的指定端口号。
  2. 常用方法
    void close()
  3. 关闭此套接字。
//新建Socket对象,连接指定ip的服务器的指定端口
Socket s = new Socket(ip, port);

//从Socket获取双向的流
InputStream in = s.getInputStream();
OutputStream out = s.getOutputStream();
4、测试案例

需求:-- 服务器端接收客户端发来的hello,并给客户端响应hello

  • 服务器端

说明其中,server端的accept()是阻塞的,客户端不连接,服务器不执行后面流程。
in.read()也是阻塞的,读不到就死等。

package cn.tedu.net;

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

//这是socket编程的服务器端
public class Server {
	public static void main(String[] args) throws Exception {
		//1、创建服务器对象,表示在该端口上,等待客户端连接请求
		//参数是端口号0-65535,其中0-1024被系统占用
		ServerSocket ss = new ServerSocket(9000);
		
		//2、开始接受客户端的请求,并建立了数据传输通道socket
		Socket socket = ss.accept();
		System.out.println("恭喜您,连接成功111");
		
		//3、接收客户端发来的数据
		InputStream in = socket.getInputStream();
		//客户端发来5个字节。循环读取5次。
		for (int i = 0; i < 5; i++) {
//			int b = in.read();//不能读到整数,我就要字符
			char c = (char)in.read();
			System.out.print(c);//同行展示
		}
		
		//4、服务端给客户端发送数据 -- out 
		OutputStream out = socket.getOutputStream();
		
		//5、开始写出数据
		out.write("world".getBytes());
		out.flush();
		
	}
}

  • 客户端
package cn.tedu.net;

import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

//这是socket编程的客户端
public class Client {
	public static void main(String[] args) throws Exception {
		//1、创建客户端对象,连接指定的服务器
		//参数是服务器的IP和服务器的端口
		//IP地址如果是访问你这台电脑IP是固定值127.0.0.1/localhost
		//IP地址如果是在工作中,就应该写真实的服务器的IP
//		Socket sc = new Socket("192.168.1.50",20235);
		Socket so = new Socket("127.0.0.1",9000);
		
		//2、给服务器发送数据 -- out
		OutputStream out = so.getOutputStream();
		
		//3、开始写出数据
		out.write("hello".getBytes());//参数需要的是byte
		out.flush();
		
		//4、客户端读取服务器发回来的数据 -- in
		InputStream in = so.getInputStream();
		for (int i = 0; i < 5; i++) {
			char c = (char) in.read();
			System.out.print(c);
		}
		
	}
}

  • 测试
  1. 先启动服务器端
  2. 在启动客户端
  3. 服务器端 或者 客户端 只能启动一次。多次就会抛出端口占用异常:java.net.BindException: Address already in use: JVM_Bind
  4. 注意启动顺序,如果反了,客户端无法连接服务器,会抛出异常: java.net.ConnectException: Connection refused: connect

在这里插入图片描述

【总结】

  • 接下来需要做一个系统的总结了,有针对性的进行总结,比如面试重点、易混点…
  • 推荐总结:数组 + OOP + IO + 集合
  • 可以稍微了解一下算法:冒泡排序(复习数组) + jdk新特性
  • 又到了兔八哥说再见的时候了,┏(^0^)┛!!!点赞加关注,学习不迷路;
  • 我们下期再见!!!
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

经理,天台风好大

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值