【导入】
- 本篇讲解的一些是作为新手入职之后不经常用到的,但是可能会面试被问到,因此需要理解,以后用到的时候在深入研究。但是不作为现阶段研究的重点,切勿跑偏方向;
- 到目前为止,java基础的学习就到这个地方,没有写的很全面,还需要后期的补充。有蒙圈的哥们,可以自行复习,接下来就是一点点的web前端知识,还有Maven的研究了。
- 一起加油!!!ヾ(◍°∇°◍)ノ゙
【目录】
一、注解
1、概述
- 注解很厉害,它可以增强我们的java代码,同时利用反射技术可以扩充实现很多功能。它们被广泛应用于三大框架底层。传统我们通过xml文本文件声明方式,而现在最主流的开发都是基于注解方式,代码量少,框架可以根据注解去自动生成很多代码,从而减少代码量,程序更易读。例如最火爆的SpringBoot就完全基于注解技术实现;
- 注解设计非常精巧,初学时觉得很另类甚至多余,甚至垃圾。有了java代码干嘛还要有@注解呢?但熟练之后你会赞叹,它竟然可以超越java代码的功能,让java代码瞬间变得强大。大家慢慢体会吧;
- 常见的元注解:@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
注解为可重复类型注解,可以在同一个地方多次使用,不常用
- 常用的
@Target
和@Retention
- 如果觉得这十个还不够用,还可以自定义注解
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对象
反射对象有三种方式
Class.forName(“类的全路径”);
类名.class
对象.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、服务器端
- 在服务器端,选择一个端口号,在指定端口上等待客户端发起连接。
- 创建对象
ServerSocket(int port)
- 常用方法
Socket accept()
- 侦听并接受到此套接字的连接。
void close()
- 关闭此套接字。
ServerSocket ss = new ServerSocket(9000);//启动服务
Socket socket = ss.accept();//等待客户端发起连接,并建立连接通道
3、客户端
- 创建对象
Socket(String host, int port)
创建一个流套接字并将其连接到指定主机上的指定端口号。 - 常用方法
void close()
- 关闭此套接字。
//新建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);
}
}
}
- 测试
- 先启动服务器端
- 在启动客户端
- 服务器端 或者 客户端 只能启动一次。多次就会抛出端口占用异常:
java.net.BindException: Address already in use: JVM_Bind
- 注意启动顺序,如果反了,客户端无法连接服务器,会抛出异常:
java.net.ConnectException: Connection refused: connect
【总结】
- 接下来需要做一个系统的总结了,有针对性的进行总结,比如面试重点、易混点…
- 推荐总结:数组 + OOP + IO + 集合
- 可以稍微了解一下算法:冒泡排序(复习数组) + jdk新特性
- 又到了兔八哥说再见的时候了,┏(^0^)┛!!!点赞加关注,学习不迷路;
- 我们下期再见!!!