java学习之路(自用)

前言

考研失败,面临着找工作,只好重新复习下java基础,并加强学习。


jar包使用

  1. 复制jar包到lib

  1. 添加到lib

  1. 给项目添加依赖

  1. 生成artifact部署到tomcat

IDEA(mac)快捷键

快捷键

说明

command + delete

删除当前行

command + D

复制当前行

command + ←/→

光标调到行首/行尾

option + ←/→

光标位置到单词头/尾

shift + option + ←/→

选中光标位置到单词头/尾

command + shift + ↑/↓

当前行上移/下移

control + /

添加/删除注释

option + 回车

导包

conmmand + option + L

格式化代码

control + R

运行程序

control + D

debug

fn + F8

逐行执行

fn + F7

进入代码

command + N

构造器

option + conmmand + T

Try-Catch

23/01/03(类、包、访问修饰符、封装、继承)

一个 Java 源文件中可以定义多个类,但是最多只有一个类被 public 修饰,并且这个类的类名与文件名必须相同。若这个文件中没有 public 的类,则文件名可随便命名(前提是符合规范)。要注意的是,当用 javac 指令编译有多个类的 Java 源文件时,它会给该源文件中的每一个类生成一个对应的 .class 文件。

包的本质 实际上就是创建不同的文件夹来保存类

命名规范 com.公司名.项目名.业务模块

访问修饰符

同类

同包

子类

不同包

public

protected

默认

private

封装

1.属性私有化

2.提供公共的get/set方法

继承

解决代码复用性,当多个类存在共同的属性和方法,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,因此所有的子类不需要重新定义这些属性和方法。(子类自动拥有父类定义的属性和方法)

基本语法 class 子类 extends 父类

detail

1.子类必须调用父类的构造器(super();),完成父类的初始化。

2.创建子类对象时,默认总会调用父类无参构造器,如果父类没有无参构造器,则必须用super指定使用父类的哪个构造器完成初始化。

23/01/04(多态、equals方法、房屋出租系统)

多态

方法的多态

1.重载(形参不同)

2.重写(覆盖)修饰符 返回值类型 方法名(参数列表)要完全一致。

对象的多态(前提是两个 类/对象 有继承关系)

1.对象编译类型和运行类型可以不一致。

2.编译类型在定义时就确定了,不能改变。(编译类型可以指向其子类的运行类型)

3.运行类型可以变化。

4.编译类型看定义时=号左边,运行类型看=号右边。

向上转型

父类引用指向子类的对象

可以调用父类的所有成员,不能调用子类的特有成员,最终运行效果看子类如何实现。

Animal animal = new cat();

向下转型(把父类引用强转成子类类型)

子类类型 引用名 = (子类类型)父类引用

属性没有重写(直接看编译类型,此处和方法不同)

动态绑定

方法看运行类型,属性哪里调用就用哪里

equals方法

只能判断引用类型,默认判断地址是否相等,子类往往重写该方法,用于判断内容是否相等。

23/01/05(类变量/类方法、main方法、代码块、类加载、final、抽象类、接口)

类变量/静态变量

也称为静态变量,会被该类的所有对象实例共享。

  1. 类变量随着类的加载而创建,即使没有创建对象实例也可以访问。

  1. 依然遵守访问权限。

  1. 类变量在类加载时就初始化了。

  1. 类变量随类的加载开始,随类的消亡而销毁。

类方法/静态方法

  1. 静态方法只能调用静态变量(普通方法静态和非静态变量都可以访问)

  1. 静态方法中不能使用this

  1. 一些通用的方法常背设置成静态的(如工具类),即不被创建对象实例直接使用

  1. 要访问非静态变量就需要先创建实例,再去访问

代码块/静态代码块

属于类中的成员

  1. 和方法不同,没有方法名,没有返回值,没有参数,只有方法体,只随着类加载或创建对象时隐式调用。

  1. 相当于另外一种形式的构造器,可以做到初始化的操作(如多个构造器有重复的语句可以提取到代码块中)

  1. 静态代码块,随着类的加载而执行且只执行一次,普通代码块,每创建一个对象就执行一次

类加载

1.创建对象实例时

2.创建子类对象实例,父类也会被加载

3.使用类的静态成员时(静态属性、静态方法)

代码块调用顺序

1.静态代码块和静态属性初始化

2.普通代码块和普通属性初始化

3.调用构造方法

main方法

  1. java虚拟机调用main方法,所以必须是public(因为虚拟机和main不在一个类)

  1. 因为需要不创建实例对象就执行,所以是static

final

  1. 类希望不被继承时

  1. 希望方法不被子类重写时

  1. 希望类的某个属性不被修改时

抽象类

父类的某些方法需要声明,又不确定如何实现,可以将其声明为抽象方法,该类就是抽象类。

1.抽象类不能实例化
2.抽象类不一定包含abstract方法
3.一旦包含abstract方法,则该类必须声明为abstract
4.final、static、private不能重写
5.继承抽象类的非需要实现所有的抽象方法

接口

普通类实现接口,必须将该接口的所有方法实现
抽象类实现接口可以不用实现接口的方法
接口属性只能是final的,而且是public static final

接口的多态性

1.接口引用可以指向实现了接口的类的对象

23/01/11(四种内部类、枚举)

四种内部类

1.定义在外部类的局部位置上(比如方法内)

  1. 局部内部类(有类名)

使用方法:

a.可以访问外部类的所有成员,包括私有的。
b.不想被继承可以使用final
c.如果变量和外部类重名,并在内部类中想使用外部类的变量(外部类名.this.变量名)
  1. 匿名内部类(无类名并且同时是个对象)

基本语法:

new 类/接口 (参数列表){
    类体
};

细节:

IA tiger = new IA(){
    类体
}  //1.jdk底层创建了实现IA接口的匿名内部类的对象地址返回给tiger.
   //2.匿名内部类创建一次就没有了,对象可以反复使用。
1. jdk底层创建了 实现/继承IA 接口/类/抽象类的匿名内部类的对象地址返回给tiger.
2.匿名内部类创建一次就没有了,对象可以反复使用。

2.定义在外部类的成员位置

  1. 成员内部类(无static修饰)
  1. 静态内部类(有static修饰)

枚举

特点:

1.枚举属于一种特殊的类,只包含一组有限的特定对象。
2.只读不可修改。

1.自定义实现枚举

步骤:

1.构造器私有化,防止new。
2.去掉set相关方法,防止属性被修改。
3.直接在类的内部创建固定的对象。

2.enum关键字实现枚举

enum Season02 {
SPRING("春天","温暖"),WINTER("冬天","寒冷");

    private String name;
    private String desc;

    private Season02(String name, String desc) {
        this.name = name;
        this.desc = desc;
    }
}

特点:

1.默认继承Enum类,所以不能再继承其他类。(因为java单继承机制)
2.public static final Season SPRING = new Season("春天","温暖");

23/01/12(注解、异常、包装类、String类、集合、泛型)

注解(Annotation)

@Override 表示重写了父类的方法(从编译层面验证,如果父类没有该方法,则报错。)
@Target 修饰注解的注解,称为元注解。
@Deprecated 表示该方法已废弃。
@SuppressWarnings 编译器抑制警告。

异常处理

1.try-catch-finally 捕获异常自行处理。(finally的内容始终都要执行)

2.throws 将发生的异常抛出,交给调用者处理。

3.自定义异常

包装类

针对八种基本数据类型相应的引用类型-包装类
有了类的特点就可以调用类中的方法
八种基本数据类型:byte、short、int、long、float、double、boolean、char

1.包装类和基本数据类型的转换

//手动装箱
int n1 = 100;
//方法1
Integer integer = new Integer(n1);
//方法2
Integer integer1 = Integer.valueOf(n1); 

//手动拆箱
int i = integer.intValue();
        
//jdk5后可以自动装箱与拆箱

//自动装箱
int n2 = 100;
Integer integer2 = n2; //底层使用的Integer.valueOf(n2);

//自动拆箱
int n3 = integer2; //底层使用的是intValue();

2.包装类和String类型相互转换

//包装类 -> String
Integer i =100;
//方法1
String str1 = i + "";
//方法2
String str2 = i.toString();
//方式3
String str3 = String.valueOf(i);

//String -> 包装类
//方法1
String str4 = "12345";
Integer i2 = Integer.parseInt(str4);
//方法2
Integer i3 = new Integer(str4);

String类

1.String对象创建方式

//方式1
String s = "abc";
//方式2
String s2 = "abc";
方法1 先查看常量池中是否有"abc"的数据空间,如果有就直接指向,如果没有则创建再指向。s最终指向的是"abc"在常量池的空间地址。

方法2 现在堆中创建空间,里面维护了value属性,指向"abc"在常量池的空间,如果常量池中有"abc"的数据空间,就通过value指向,如果没有则创建再指向。value最终指向的是"abc"在常量池的空间地址。

2.String类的常见方法

3.StringBuilder类

4.StringBuffer类

Arrays类

集合

Collection接口

遍历方式

1.Iterator
2.增强for
List接口
元素有序(添加顺序与取出顺序一致)且可重复

常用方法:

//1.在index位置插入element元素
void add(int index,Object element)

//2.在index位置开始将element中的所有元素添加进来
boolean addAll(int index,Collection elements)

//3.获取index位置的元素
Object get(int index)

//4.返回obj在集合中首次出现的位置
int indexOf(Object obj)

//5.返回obj在集合中最后出现的位置
int lastIndexOf(Object obj)

//6.移除index位置的元素并返回
Object remove(int index)

//7.将index位置的元素替换为element
Object set(int index,Object element)

//8.返回从fromIndex到toIndex位置的子集合(前闭后开)
List subList(int fromIndex,int toIndex)
ArrayList类
底层 数组实现的
基本等同于Vector,但ArrayList是线程不安全的(执行效率高)
Vector类
底层也是 数组实现的
是线程同步的
LinkedList类
底层实现了 双向链表双端队列
线程不安全
ArrayList和LinkedList的选择
改查多选择ArrayList
增删多选择LinkedList
Set接口
无序没有索引,且不可重复,所以最多包含一个null
HashSet

底层其实是HashMap

老韩一直讲源码有些遭不住了,先去看泛型了

泛型

约束了元素的类型
减少了类型转换次数

23/01/13(线程、I/O流)

线程

main线程中开启thread线程
start底层会创建新的线程,run只是一个方法调用,不会启动新线程

1.创建线程的两种方式

1.继承Thread类

public class Animal{
    public static void main(String[] args) {
        Cat cat = new Cat();
        cat.start();
    }
}

class Cat extends Thread {
    @Override
    public void run() {
        super.run();
    }
}

2.实现Runable接口

public class Animal{
    public static void main(String[] args) {
        Dog dog = new Dog();
        Thread thread = new Thread(dog);
        thread.start();
    }
}

class Dog implements Runnable {
    @Override
    public void run() {
        while (true){
            System.out.println("线程启动了");
        }
    }
}

2.线程终止

设置一个loop变量,在main线程修改成false。

3.线程中断

t.interrupt();

4.线程插队

yield:线程的礼让,不一定成功
join:线程的插队,一旦插队成功就肯定先执行完插入线程的所有任务

5.用户线程和守护线程

用户线程:也叫工作线程,当线程的任务执行完以通知的方式来结束
守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束
常见的守护线程:垃圾回收机制
设置守护线程: 类名.setDaemon(true);

6.线程的生命周期

7.线程的同步

Synchronized

同步:在任何同一时刻,最多只有一个线程访问,以保证数据的完整性

线程去对象那看互斥锁是否闲置,锁要求是同一个对象!

1.同步代码块
synchronized(this){
}
2.同步方法
public synchronized void m (Stirng name) {
}
3.同步静态方法
public static void m1() {
    synchronized(类名.class){
    }
}

8.互斥锁

每个对象都有一个可称为"互斥锁的标记"

9.线程死锁

10.释放锁

线程的同步方法、同步代码块执行完毕
线程的同步方法、同步代码块遇到break、return
线程的同步方法、同步代码块遇到未处理的Error或Exception
线程的同步方法、同步代码块执行了线程对象的wait()方法,当前线程暂停并释放锁

I/O流

java->文件 输出
java<-文件 输入

1.创建文件

//1.根据路径构建一个File对象
public File(String pathname)
//2.根据路径+名称构建
public File(String parent, String child)
//3.根据父目录+子路径构建
public File(File parent, String child)

方式1

public static void create01() throws IOException {
    String filePath = "/Users/Timetravel/Desktop/news1.txt";
    File file = new File(filePath);
    file.createNewFile();
}

方式2

public static void create02() throws IOException {
    File parentFile = new File("/Users/Timetravel/Desktop");
    String fileName = "news2.txt";
    File file = new File(parentFile, fileName);
    file.createNewFile();
}

抽象基类

字节流

字符流

输入流

InputStream

Reader

输出流

OutputStream

Writer

2.InputStream&OutputStream

字节
1.FileInputStream&FileOutputStream
字节输入流 作用:文件->程序
FileInputStream

1.使用单个字节的读取文件

String filePath = "/Users/Timetravel/Desktop/news1.txt";
int readData = 0;
FileInputStream fileInputStream = new FileInputStream(filePath);
//fileInputStream.read() 如果返回-1代表读取完毕
while ((readData = fileInputStream.read()) != -1) {
    System.out.print((char) readData);
}
//最后要关闭文件流,释放资源
fileInputStream.close();

2.使用read(byte[] b)读取文件

String filePath = "/Users/Timetravel/Desktop/news1.txt";
//readLen记录偏移次数
int readLen = 0;
//一次读8个字节
byte[] buf = new byte[8];
FileInputStream fileInputStream = new FileInputStream(filePath);
//fileInputStream.read() 如果返回-1代表读取完毕
while ((readLen = fileInputStream.read(buf)) != -1) {
    //读取从0开始到buf.length索引的位置的数据
    System.out.printf(new String(buf,0, readLen));
}
fileInputStream.close();
FileOutputStream

1.写入一个字节(覆盖式)

String filePath = "/Users/Timetravel/Desktop/news1.txt";
FileOutputStream fileOutputStream = new FileOutputStream(filePath);
//写入一个字节
fileOutputStream.write('A');
fileOutputStream.close();

2.写入字符串(覆盖式)

String filePath = "/Users/Timetravel/Desktop/news1.txt";
String str = "hello,world";
FileOutputStream fileOutputStream = new FileOutputStream(filePath);
//str.getBytes 字符串->数组
fileOutputStream.write(str.getBytes());
fileOutputStream.close();

3.指定读取数组的位置(覆盖式)

String filePath = "/Users/Timetravel/Desktop/news1.txt";
String str = "hello,world";
FileOutputStream fileOutputStream = new FileOutputStream(filePath);
//指定从数组中读取的起始和结束位置
fileOutputStream.write(str.getBytes(),0,str.length());
fileOutputStream.close();

4.追加式写入

String filePath = "/Users/Timetravel/Desktop/news1.txt";
//FileOutputStream(filePath,true) 为追加式
FileOutputStream fileOutputStream = new FileOutputStream(filePath,true);
//写入一个字节
fileOutputStream.write('A');
fileOutputStream.close();
2.ObjectInputStream&ObjectOutputStream
序列化=同时保存值与数据类型
序列化需要 1.Serializable(是一个标记接口) 2.Externalizable
ObjectOutputStream 提供序列化功能
ObjectInputStream 提供反序列化功能
serialVersionUID 序列化的版本号,提高版本兼容性

序列化对象时,要求里面的属性也要实现序列化接口
序列化具备可继承性,父类实现序列化,则所有子类均默认实现序列化
static和transient修饰的成员不会被序列化
ObjectOutputStream
public static void objectOutputStream01() throws IOException {
    //序列化后保存格式不是纯文本
    String filePath = "/Users/Timetravel/Desktop/news1.dat";
    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath));
    //序列化数据保存
    //int -> Integer
    oos.write(100);
    //boolean -> Boolean
    oos.writeBoolean(true);
    //char -> Character
    oos.writeChar('a');
    //保存一个Dog对象
    oos.writeObject(new Dog("旺财",10));
    oos.close();
}
ObjectInputStream
public static void objectInputStream01() throws IOException, ClassNotFoundException {
    String filePath = "/Users/Timetravel/Desktop/news1.dat";
    ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath));
    System.out.println(ois.readInt());
    System.out.println(ois.readBoolean());
    System.out.println(ois.readChar());
    Object o = ois.readObject();
    System.out.println("运行类型=" + o.getClass());
    System.out.println("dog信息=" + o);
    ois.close();
}

3.BufferedInputStream&BufferedOutputStream
属于字节处理流
public static void bufferedInputStream01() throws IOException {
    String srcFilePath = "/Users/Timetravel/Desktop/news1.txt";
    String destFilePath =  "/Users/Timetravel/Desktop/news2.txt";
    BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFilePath));
    BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFilePath));
    byte[] buff = new byte[1024];
    int readLen = 0;
    while ((readLen = bis.read(buff)) != -1) {
        bos.write(buff,0,readLen);
    }
    bis.close();
    bos.close();
}

2.Reader&Writer

字符
1.FileReader&FileWriter
字符输入流
FileReader

1.每次读取一个字符

public static void readFile01() throws IOException {
    String filePath = "/Users/Timetravel/Desktop/news1.txt";
    FileReader fileReader = new FileReader(filePath);
    int data = 0;
    //1.每次读取一个字符
    while ((data = fileReader.read()) != -1 ) {
        System.out.print((char) data);
    }
    fileReader.close();
}

2.每次读取多个字符

public static void readFile02() throws IOException {
    String filePath = "/Users/Timetravel/Desktop/news1.txt";
    FileReader fileReader = new FileReader(filePath);
    int reaLen = 0;
    char[] buf = new char[8];
    //2.每次读取8个字符
    //.read(buf)返回的是实际读取到的字符数
    while ((reaLen = fileReader.read(buf)) != -1 ) {
        System.out.print(new String(buf,0,reaLen));
    }
    fileReader.close();
}
FIleWriter
字符输出流
public static void fileWriter01() throws IOException {
    String filePath = "/Users/Timetravel/Desktop/news1.txt";
    //FileWriter(filePath);默认覆盖式写入
    FileWriter fileWriter = new FileWriter(filePath);
    char[] chars = {'a','b','c'};
    //1.写入单个字符
    fileWriter.write("C");
    //2.写入指定数组
    fileWriter.write(chars);
    //3.写入数组的指定部分
    fileWriter.write("我是个大好人!".toCharArray(),0,3);
    fileWriter.write(chars,0,3);
    //4.写入整个字符串
    fileWriter.write("你好世界");
    //5.写入字符串的指定部分
    fileWriter.write("你好",0,1);
    fileWriter.close();
}
2.BufferedReader&BufferedWriter
BufferedReade
属于字符处理流
public static void bufferedReader01() throws IOException {
    String filePath = "/Users/Timetravel/Desktop/news1.txt";
    BufferedReader bufferedReader = new BufferedReader(new FileReader(filePath));
    //按行读取
    String line;
    //当bufferedReader.readLine()返回一个null时读取完毕
    while ((line = bufferedReader.readLine()) != null) {
        System.out.println(line);
    }
    bufferedReader.close();
}
BufferedWriter
属于字符处理流
public static void BufferedWriter01() throws IOException {
    String filePath = "/Users/Timetravel/Desktop/news1.txt";
    BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(filePath));
    bufferedWriter.write("hello,world");
    //换行
    bufferedWriter.newLine();
    bufferedWriter.close();
}
3.InputStreamReader&OutputStreamWriter
转换流
public static void inputStreamReader_() throws FileNotFoundException, UnsupportedEncodingException {
    String filePath = "/Users/Timetravel/Desktop/news1.txt";
    //1.把FileInputStream 转成 InputStreamReader 并指定编码
    InputStreamReader gbk = new InputStreamReader(new FileInputStream(filePath), "gbk");
    //2.把InputStreamReader 传入 BufferedReader字符处理流
    BufferedReader bufferedReader = new BufferedReader(gbk);
    bufferedReader.colse();
}

3.节点流&处理流

节点流:从特定的数据源读取数据
处理流:也称"包装流",对节点流进行包装,使用了修饰器设计模式,不会直接与数据源相连
处理流的优势:
1.性能的提高,增加缓冲的方式来提高I/O的效率
2.操作的便捷,处理流提供了一系列便捷的方法来一次I/O大批量的数据

TCP网络通信

  1. InetAddress

  1. InetAddressd的使用

  1. 获取本机的InetAddress对象
//1.获取本机的InetAddress对象
InetAddress host01 = InetAddress.getLocalHost();

//2.获取指定主机名的InetAddress对象
InetAddress host02 = InetAddress.getByName("MacBook-Pro");

//3.获取指定域名的InetAddress对象
InetAddress host03 = InetAddress.getByName("www.baidu.com");
  1. 通过InetAddress对象,获取相应的地址
String hostAddress = host03.getHostAddress();
  1. 通过InetAddress对象,获取相应主机名/域名
String hostName = host03.getHostName();
  1. Socket

  1. SocketTCP应用案例01

client

public class SocketTCP01Client {
    public static void main(String[] args) throws IOException {
        //1.连接本机的9999端口,并返回socket对象
        Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
        //2.获取和socket对象关联的输出流对象
        OutputStream outputStream = socket.getOutputStream();
        //3.通过输出流对象,写入数据到数据通道
        outputStream.write("hello!server".getBytes());
        //4.关闭流对象和socket
        outputStream.close();
        socket.close();
        System.out.println("客户端关闭....");
    }
}

server

public class SocketTCP01Server {
    public static void main(String[] args) throws IOException {
        //1.在本机9999端口监听并等待连接,要求本机没有其他服务在占用9999端口
        ServerSocket serverSocket = new ServerSocket(9999);
        //2.serverSocket.accept()当有客户端连接的时候会返回一个Socket对象
        //如果没有客户端连接,程序会阻塞
        Socket socket = serverSocket.accept();
        //3.获取和socket对象关联的输入流对象
        InputStream inputStream = socket.getInputStream();
        //4.通过输入流对象,读取数据通道数据
        byte[] buf = new byte[1024];
        int readLen = 0;
        while ((readLen = inputStream.read(buf)) != -1) {
            System.out.println(new String(buf,0,readLen));
        }
        //5.关闭流和socket以及serverSocket
        inputStream.close();
        socket.close();
        serverSocket.close();
    }
}
  1. SocketTCP应用案例02(字节流)

client

public class SocketTCP02Client {
    public static void main(String[] args) throws IOException {
        //1.连接本机的9999端口,并返回socket对象
        Socket socket = new Socket(InetAddress.getLocalHost(), 9998);
        //2.获取和socket对象关联的输出流对象
        OutputStream outputStream = socket.getOutputStream();
        //3.通过输出流对象,写入数据到数据通道
        //socket.shutdownOutput()发送结束标记
        outputStream.write("hello!server".getBytes());
        socket.shutdownOutput();
        //4.获取和socket对象关联的输入流对象
        InputStream inputStream = socket.getInputStream();
        //5.通过输入流对象,从数据通道读取数据
        byte[] buf = new byte[1024];
        int readLen = 0;
        while ((readLen = inputStream.read(buf)) != -1) {
            System.out.println(new String(buf,0,readLen));
        }
        //4.关闭流对象和socket
        outputStream.close();
        inputStream.close();
        socket.close();
        System.out.println("客户端关闭....");
    }
}

server

public class SocketTCP02Server {
    public static void main(String[] args) throws IOException {
        //1.在本机9999端口监听并等待连接,要求本机没有其他服务在占用9999端口
        ServerSocket serverSocket = new ServerSocket(9998);
        //2.serverSocket.accept()当有客户端连接的时候会返回一个Socket对象
        //如果没有客户端连接,程序会阻塞
        Socket socket = serverSocket.accept();
        //3.获取和socket对象关联的输入流对象
        InputStream inputStream = socket.getInputStream();
        //4.通过输入流对象,读取数据通道数据
        byte[] buf = new byte[1024];
        int readLen = 0;
        while ((readLen = inputStream.read(buf)) != -1) {
            System.out.println(new String(buf,0,readLen));
        }
        //5.获取和socket对象关联的输出流对象
        OutputStream outputStream = socket.getOutputStream();
        outputStream.write("hello,client".getBytes());
        socket.shutdownOutput();
        //6.关闭流和socket以及serverSocket
        inputStream.close();
        outputStream.close();
        socket.close();
        serverSocket.close();
        System.out.println("服务端关闭....");
    }
}

  1. SocketTCP应用案例03(字符流)

client

public class SocketTCP03Client {
    public static void main(String[] args) throws IOException {
        //1.连接本机的9999端口,并返回socket对象
        Socket socket = new Socket(InetAddress.getLocalHost(), 9998);
        //2.获取和socket对象关联的输出流对象
        OutputStream outputStream = socket.getOutputStream();
        //3.通过输出流对象,写入数据到数据通道(字符流),使用了OutputStreamWriter转换流
        //socket.shutdownOutput()发送结束标记 字节流
        //bufferedWriter.newLine();发送结束标记 字符流,要求对方相应使用readLine()
        //如果使用字符流需手动刷新,否则数据不会写入通道
        BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));
        bufferedWriter.write("heelo,server 字符处理流");
        bufferedWriter.newLine();
        bufferedWriter.flush();
        //4.获取和socket对象关联的输入流对象
        InputStream inputStream = socket.getInputStream();
        //5.通过输入流对象,从数据通道读取数据(字符流)
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        System.out.println(bufferedReader.readLine());
        //4.关闭流对象和socket
        bufferedReader.close();
        bufferedWriter.close();
        socket.close();
        System.out.println("客户端关闭....");
    }
}

server

public class SocketTCP03Server {
    public static void main(String[] args) throws IOException {
        //1.在本机9999端口监听并等待连接,要求本机没有其他服务在占用9999端口
        ServerSocket serverSocket = new ServerSocket(9998);
        //2.serverSocket.accept()当有客户端连接的时候会返回一个Socket对象
        //如果没有客户端连接,程序会阻塞
        Socket socket = serverSocket.accept();
        //3.获取和socket对象关联的输入流对象
        InputStream inputStream = socket.getInputStream();
        //4.通过输入流对象,读取数据通道数据(字符流)使用了OutputStreamReader转换流
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        System.out.println(bufferedReader.readLine());
        //5.获取和socket对象关联的输出流对象
        OutputStream outputStream = socket.getOutputStream();
        //6.回复信息(字符流)
        BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));
        bufferedWriter.write("hello client 字符流");
        bufferedWriter.newLine();
        bufferedWriter.flush();
        //7.关闭流和socket以及serverSocket
        bufferedReader.close();
        bufferedWriter.close();
        socket.close();
        serverSocket.close();
        System.out.println("服务端关闭....");
    }
}

  1. 文件上传

client

public class TCPFileUploadClient {
    public static void main(String[] args) throws Exception {
        //客户端连接服务端
        Socket socket = new Socket(InetAddress.getLocalHost(),8888);
        //创建读取磁盘文件的输入流
        String filePath = "/Users/Timetravel/Desktop/cpx01.jpg";
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filePath));
        //bytes 就是文件对应的字节数组
        byte[] bytes = StreamUtils.streamToByteArray(bis);
        //通过Socket获取到输出流,将bytes数据发送给数据通道
        BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
        bos.write(bytes);
        bis.close();
        socket.shutdownOutput();
        //接收服务端的消息
        InputStream inputStream = socket.getInputStream();
        String s = StreamUtils.streamToString(inputStream);
        System.out.println(s);
        inputStream.close();
        bos.close();
        socket.close();
    }
}

server

public class TCPFileUploadServer {
    public static void main(String[] args) throws Exception {
        //1.服务端在本机监听8888端口
        ServerSocket serverSocket = new ServerSocket(8888);
        System.out.println("服务端在8888端口监听....");
        //2.等待连接
        Socket socket = serverSocket.accept();
        //3.读取客户端发送的数据
        BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
        byte [] bytes = StreamUtils.streamToByteArray(bis);
        //4.将得到的数据写到指定路径
        String destFilePath = "src/cpx02.jpg";
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFilePath));
        bos.write(bytes);
        bos.close();
        //5.向客户端回复"收到"
        BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        bufferedWriter.write("收到!");
        bufferedWriter.flush();
        socket.shutdownOutput();

        bufferedWriter.close();
        bis.close();
        socket.close();
        serverSocket.close();
    }
}

反射

  1. 获得Class类的5种方式

  1. 通过对象获得

Class c1 = person.getClass();
  1. 通过forname获得

Class c2 = Class.forName("com.cpx.upload.Student");
  1. 通过类名.class获得

Class c3 = Student.class;
  1. 基本包装类都有一个Type属性

Class c4 = Integer.TYPE;
  1. 获取父类Class类对象

Class c5 = c1.getSuperclass();
  1. 可以有Class类对象的类型

  1. class

  1. interface

  1. []

  1. eunm

  1. annotation

  1. primitiye type

  1. void

  1. 通过反射,动态创建对象

//1.获得Class类对象
Class c1 = Class.forName("com.cpx.upload.Person");
//2.1通过Class类对象创建一个对象
Person person = (Person)c1.newInstance();
//2.2通过Class类对象使用构造器创建一个对象
Constructor constructor = c1.getConstructor(String.class);
Person person1 = (Person)constructor.newInstance("张三");
System.out.println(person1.name);
//3.通过Class类对象调用普通方法
Method hi = c1.getDeclaredMethod("hi",String.class);
hi.invoke(person1,"李四");
//4.私有属性需要设置setAccessible()方法
Field name = c1.getDeclaredField("name");
name.setAccessible(true);
name.set(person1,"王五");
getMethod():获取自身能用所有的公共方法。1.类本身的public 2.继承父类的public 3.实现接口的public
getDeclaredMethod():获取类自身声明的所有方法。

JDBC

  1. 使用步骤

//1.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取连接
Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/JDBC","root","cpx19970902");
//3.创建statement
Statement statement = connection.createStatement();
//4.发送sql语句并返回结果
String sql = "select * from t_user;";
ResultSet resultSet = statement.executeQuery(sql);
//5.进行结果解析
while (resultSet.next()) {
    int id = resultSet.getInt("id");
    String account = resultSet.getString("account");
    String password = resultSet.getString("password");
    String nickname = resultSet.getString("nickname");
    System.out.println(id + "--" + account + "--" + nickname);
}
//6.关闭源
resultSet.close();
statement.close();
connection.close();
  1. PreparedStatement

 解决注入攻击及SQL语句拼接问题
注入攻击:把值写成查询关键字的一部分
Scanner scanner = new Scanner(System.in);
System.out.println("请输入账号");
String account = scanner.nextLine();
System.out.println("请输入密码");
String password = scanner.nextLine();

Class.forName("com.mysql.cj.jdbc.Driver");
Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/JDBC?user=root&password=cpx19970902");

String sql = "select * from t_user where account = ? and password = ?;";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setObject(1,account);
preparedStatement.setObject(2,password);

ResultSet resultSet = preparedStatement.executeQuery();

if (resultSet.next()) {
    System.out.println("登陆成功");
} else {
    System.out.println("登陆失败");
}
  1. JDBC扩展

  1. 自增长主键回显实现

//1.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取连接
Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/JDBC?user=root&password=cpx19970902");
//3.编写SQL语句
String sql = "insert into t_user(account,password,nickname) values(?,?,?);";
//4.创建statement,并在形参传入Statement.RETURN_GENERATED_KEYS
PreparedStatement preparedStatement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
//5.占位符赋值
preparedStatement.setObject(1,"test2");
preparedStatement.setObject(2,"123456");
preparedStatement.setObject(3,"吕丹丹");
//6.发送SQL语句
int i = preparedStatement.executeUpdate();
//7.结果解析
if (i > 0) {
    System.out.println("数据插入成功");
    ResultSet generatedKeys = preparedStatement.getGeneratedKeys();
    //首先移动一行,默认为数据行的前一行,即空行
    generatedKeys.next();
    //getGeneratedKeys()返回的主键结果集只有1列
    int id = generatedKeys.getInt(1);
    System.out.println("id=" + id);
} else {
    System.out.println("数据插入失败");
}
//8.关闭资源
preparedStatement.close();
connection.close();
  1. 批量数据插入性能提升

//1.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取连接
Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/JDBC" +
        "?rewriteBatchedStatements=true","root","cpx19970902");
//3.编写SQL语句
String sql = "insert into t_user(account,password,nickname) values(?,?,?);";
//4.创建statement,并在形参传入Statement.RETURN_GENERATED_KEYS
PreparedStatement preparedStatement = connection.prepareStatement(sql);

long start = System.currentTimeMillis();

//5.占位符赋值
for (int i = 0; i < 10000; i++) {
    preparedStatement.setObject(1,"test00" + i);
    preparedStatement.setObject(2,"123456" + i);
    preparedStatement.setObject(3,"吕丹丹" + i);

    //6.不执行并追加到values后面
    preparedStatement.addBatch();
}
//7.执行批量操作
preparedStatement.executeBatch();

long end = System.currentTimeMillis();

//7.结果解析
System.out.println(end - start);
//8.关闭资源
preparedStatement.close();
connection.close();
  1. JDBC中数据库事务实现

package com.cpx.api.transaction;

import org.junit.Test;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

/**
 * @Author: Cpx
 * @Date: 2023/1/19 16:18
 * @Version:
 * @Description: 银行卡业务方法,调用dao方法
 */

public class BankService {
    @Test
    public void start() throws SQLException, ClassNotFoundException {
        transfer("lvdandan","ergouzi",500);
    }

public void transfer(String addAccount,String subAccount,int money) throws SQLException, ClassNotFoundException {
    BankDao bankDao = new BankDao();
    Class.forName("com.mysql.cj.jdbc.Driver");
    Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/JDBC?user=root" + "&password=cpx19970902");
    try {
        //关闭事务自动提交
        connection.setAutoCommit(false);
        bankDao.add(addAccount,money,connection);
        System.out.println("--------");
        bankDao.sub(subAccount,money,connection);
        //事务提交
        connection.commit();
    }catch (Exception e) {
        //事务回滚
        connection.rollback();
        throw e;
    }finally {
        connection.close();
    }

    }
}
package com.cpx.api.transaction;

import java.sql.*;

/**
 * @Author: Cpx
 * @Date: 2023/1/19 13:49
 * @Version:
 * @Description:
 */

public class BankDao {

    public void add(String account,int money,Connection connection) throws ClassNotFoundException, SQLException {
        String sql = "update t_bank set money = money + ? where account = ?;";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        preparedStatement.setObject(1,money);
        preparedStatement.setObject(2,account);
        preparedStatement.executeUpdate();
        preparedStatement.close();
        System.out.println("加账成功");
    }

    public void sub(String account,int money,Connection connection) throws ClassNotFoundException, SQLException {
        String sql = "update t_bank set money = money - ? where account = ?;";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        preparedStatement.setObject(1,money);
        preparedStatement.setObject(2,account);
        preparedStatement.executeUpdate();
        preparedStatement.close();
        System.out.println("减钱成功");
    }

}
  1. Druid连接池技术

  1. 硬编码方式
//1.创建一个druid连接池对象
DruidDataSource druidDataSource = new DruidDataSource();
//2.设置连接池参数 
//  必须:url,user,password
//非必须:初始化连接数,最大连接数
druidDataSource.setUrl("jdbc:mysql://127.0.0.1:3306/JDBC");
druidDataSource.setUsername("root");
druidDataSource.setPassword("cpx19970902");
druidDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
//3.获取连接(通用方法)
DruidPooledConnection connection = druidDataSource.getConnection();
//4.回收连接(通用方法):用完后放回到连接池中
connection.close();
  1. 软编码方式
//1.读取外部配置文件
Properties properties = new Properties();
InputStream resourceAsStream = DruidUsePart.class.getClassLoader().getResourceAsStream("druid.properties");
properties.load(resourceAsStream);
//2.使用连接池的工具类的工厂模式创建连接池
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
Connection connection = dataSource.getConnection();

connection.close();

配置文件

#druid连接池需要的配置参数,key固定命名
driverClassname=com.mysql.cj.jdbc.Driver
username=root
password=cpx19970902
url=jdbc:mysql://127.0.0.1:3306/JDBC
  1. 工具类封装
package com.cpx.api.utils;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

/**
 * @Author: Cpx
 * @Date: 2023/1/19 17:30
 * @Version:
 * @Description: 内部包含一个连接池对象,并且对外提供获取连接和回收连接的方法
 */

public class JdbcUtils {
    private static DataSource dataSource = null;
    private static ThreadLocal<Connection> tl = new ThreadLocal<>();
    static {
        Properties properties = new Properties();
        InputStream resourceAsStream = JdbcUtils.class.getClassLoader().getResourceAsStream("druid.properties");
        try {
            properties.load(resourceAsStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            dataSource = DruidDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static Connection getConnection() throws SQLException {
        //先检查本地变量中是否存在
        Connection connection = tl.get();
        //如果第一次没有
        if (connection == null) {
            //通过连接池获取
            connection = dataSource.getConnection();
            tl.set(connection);
        }
        return connection;
    }

    public static void freeConnection() throws SQLException {
        Connection connection = tl.get();
        if (connection != null) {
            //清空线程本地数据
            tl.remove();
            //事务状态回归
            connection.setAutoCommit(true);
            //回收到连接池
            connection.close();
        }
    }
}

ThreadLocal 可以为同一个线程存储共享变量

JavaWeb

  1. Servlet

Servlet是JavaEE规范之一 规范就是接口
Servlet是JavaWeb三大组件之一.分别是Servlet程序、filter过滤器、Listener监听器
Servlet是运行在服务器上的一个Java小程序,可以接收客户端发送的请求并响应数据给客户端
  1. Servlet实现

  1. 配置xml

<!-- servlet标签给Tomcat配置Servelt程序 -->
    <servlet>
        <!-- servlet-name标签个servlet程序起别名 -->
        <servlet-name>HelloServlet</servlet-name>
        <!-- servlet-class标签是Servlet程序的全类名 -->
        <servlet-class>com.cpx.servlet.HelloServlet</servlet-class>
    </servlet>

    <!-- 映射地址 -->
    <servlet-mapping>
        <!-- servlet-name便签告诉服务器配置的地址给那个Servlet程序使用 -->
        <servlet-name>HelloServlet</servlet-name>
        <!--url-pattern标签配置访问地址 -->
        <!-- /表示在服务器解析时 表示地址为:http://ip:port/工程路径 -->
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>
  1. Servlet生命周期

执行Servlet构造器 第一次访问创建Servlet时调用
执行init初始化方法 第一次访问创建时Servlet时调用
执行service方法 每次访问都会调用
执行destroy销毁方法 web工程停止的时候调用
  1. servlet构造器方法
  1. init初始化方法
  1. service方法
  1. destroy方法
  1. Maven

  1. Mavend坐标

goupId:定义当前Maven项目隶属组织
artifactId:定义当前Maven项目名 (通常是模块名称)
version:版本号
  1. MyBatis

MyBatis是持久层框架,用于简化JDBC开发(持久层:数据保存到数据库的那一层代码)

Spring5

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
有不少的同学发信给我,和我探讨java的自学过程应该是什么样的,毕竟有很多人因为各种各样的原因不能参加培训。我试着给出自己的见解,抛砖引玉吧。 这个路线图是给那些为了就业的人准备的,如果只是兴趣,不一定照这个走。 这里只是讲了路线图,关于路线中的各个阶段,学到什么程度,如何学习等,可以参考后面的JAVA自学之路 七:《路线图明细》。 首先要学JavaSE,这是无庸置疑的。 与此同时,是的,与此同时,和JavaSE的学习同步,建议大家研究一下数据结构与算法。 在这儿之后,你可以选择向J2ME、或者Java GUI、Socket编程等方向努力,但是通过第一篇的办法,你可以很容易就将这方向过滤掉(永远不要忘了我们是为了就业),好吧,暂时和它们诀别,还是向着J2EE的方向前进吧。 在JavaSE完成之后,可以试着完成一些小项目,同时关注一下设计模式的内容,不必强求自己能够完全掌握各种细节,往前走吧。 掌握一种编程工具,比如说Eclipse。 数据库,可以选择使用Oracle或者MySQL开始 。 JDBC,用Java连接数据库。 可以选择在这个地方切入Hibernate,也可以选择在Struts 之后。 HTML CSS JavaScript,这些东西是做web项目必需的内容 。 Servlet JSP,服务器端必需。 XML AJAX,新的流行趋势不能不了解。 作一些小项目吧。比如BBS、留言系统、学籍管理、商城等等,随便着一些业务逻辑比较简单的来做一做。 Struts,MVC的典型应用 Hibernate,OR Mapping的典范之作。 Spring IOC + AOP, 面向对象 + 面向切面,完善的旅程。 把以前自己的小项目加入Struts、Spring、Hibernate的元素吧。 去研究各种商业项目的业务逻辑吧,才能在你的简历中加入浓重的一笔。 EJB3.0,java在走了很多弯路后的回归之作,值得研究。 SOA,业界铁定的未来趋势,要紧紧跟随。 在后面,我会告诉你每一步的学习方法和学习重点。 JAVA自学之路 三:要动手 无论如何,请坚持不懈的动手实验! 学习Java要动手,仅仅是看和听是不能够学好编程的。总有同学来信问我们这样的问题,说:"老师我看了视频了,可是怎么一动手就没有任何思路呢?" 对此,我一般都会说不要着急,如果你只是看了视频就想行云流水的话,那老师这些年的锻炼真是白费了,这么多年的苦练才能略有些行云流水的感觉,你一朝一夕就想自己能够达到,不是太奢侈了吗 作为编程的过来人想一下,当年自己学编程的时候是什么时候有思路的呢?至少也是照猫画虎画了一段时间之后吧,我本人是在某一个领域画虎画了上万行代码的时候才慢慢有自己思路的。就像写作文,要有自己思路总是一般来说要经过几个阶段,首先学习词汇,然后学习造句,然后大量阅读别人的文章,自己模仿着写一些,逐渐的经验积累,才能形成自己的思路。学编程,恐怕也得慢慢的来吧,只是看一看、听一听视频,是不足以学好编程的。 所以,动手吧,跟着做,一行一行的跟着敲,一个项目敲完了,比如坦克,再试着加一些自己的功能,按照自己的思路敲一些代码,收获远比干听大的多。 如果遇到,暂时对于一些思路不能理解的同学,我一般都会劝告说,动手写,先写代码,量变会引起质变的。而这种质变的确发生过不少次。 提醒一句,要理解代码思路之后再跟着敲 ,背着敲,千万不要左边摆着别人的程序,右边自己一个一个子母的写,这就不再是程序员了,成了打字员了。 纸上得来终觉浅,所以:别问那么多,别想那么多,动手写吧。 JAVA自学之路 四:要事为先 对待人生的任何事情都要:抓大放小,要事为先 对于一个以前从来没有接触过java的人,java无疑是庞大的,似乎每个领域都可以拓展开来都是一片开阔地,每一个领域要想深入接触到每一个细节所耗费的精力都是巨大的。这个时候大家都胸怀壮志,两眼发光的盯着每一个崭新的知识点,遇见了任何一个知识点都恨不得抠的清清楚楚,明明白白。 难道这有什么不对吗? 我的意见是,没什么大毛病,但是学习效率太低了!我们推荐的学习方式是,抓大放小,要事为先。 比如说学习J2SE的时候,学到GUI这一章,有的同学认为,既然学到这儿了,我们去把SWING细细的了解一遍吧;还有的同学,遇到了在Linux下的Shell编程,就恨不得把Shell先学成专家才肯,或者是才敢,继续往下学习;有没有过这样的感觉,一本书的上册没有学好,觉着下册心里也没底儿,甚至非要把上册复习一遍才敢继续学习。如果你也是这样,要去看看心理医生啦,没准儿会有洁癖的毛病。 任何事情都要追求完美才敢继续往后进行,是一种性格缺陷 大胆的放弃一些东西吧,有失才有得,把自己有限的、宝贵的精力用在对于就业直接相关的地方,这才是最有效率的学习方式!等你参加工作,有了可持续发展的事业动力和经济基础,有时间有精力闲的没事儿干的时候,再去研究那些其它吧。 曾经有一个故事,说得是产能和产量的关系。 一个人喜欢读书,读了一辈子,无所不通,这时候要做任何工作都可以,他的产能到了极致,但是,他老了,即便每天产出大量的东西也不能维持多久了,一辈子的产量并没有多少。 另一个人直接工作,忙,天天在机器旁日复一日,做了一辈子,没时间学习和进修,可以想象,产量能有多大呢。 人的一生,要锻炼产能,也要及时产出,引擎要转,也要停下来加油,这两者平衡的越好,你的贡献和产量就会越大。如果钻研的东西和产能目标(或者说近期的产能目标)无关的话,要舍得放手。 所以,对于SWING,暂时先扔在一边吧。 对于shell,对于各种协议过于详细的细节,对于喜欢的游戏编程,对于javascript各种华而不实的效果,都暂时和它们分别吧。一切和我们的直接工作目标关联不大的东西,扔在一边或者弄清楚到足够支持下一步的学习就可以了。 那这样岂不是妨碍我成为通晓各种细节的高手了吗? 我一向不认为一个人掌握了很多很多知识点的细节就是高手了,一个人如果告诉你,回字有四种写法,你会认为他是高手吗? 毫不客气的说,现在我们所教授的所有知识细节在网上或书中都可以找到,只要你肯花时间花精力去动手试,去钻研,没有什么不能掌握的。struts、spring你能掌握吗?当然能!但是如果把时间花在spring的各种细节的研究上,花在研究中国企业应用不广泛地的spring mvc上,这个精力就太浪费了,而且学习的积极性会受到打击,谁愿意整天泡在细节的蜘蛛网中挣扎不出来呢?谁愿意天天经历风雨却总是不能见到彩虹呢? 盖房子,要先建骨架,再谈修饰。 画山水,要先画结构,再谈润色。 认识一台结构复杂的机器,应该首先认清楚脉络,然后再逐步认识每一个关节。 为了应付从小学到大学的考试,我们背了各种各样的不应该提前掌握的细节,同时也养成了见到细节就抠的学习习惯。而现在,是到改改的时候了。 请在合适的时间,做合适的事情吧。 把时间和精力花在作项目上面,花在写作品以及锻炼解决问题的能力上面吧,这是迈向高手的正确的而且快速的方向。 我一直不认为一个课程提供了很多很多的细节就是优秀的价值高的课程了,掌握必要的细节,这个不含糊,至于其他,或者通过视频给出(这样可以给出更多的细节,但是不占上课时间,课上只讲重要的、必要的细节知识),或者在掌握了自学的能力后自己能够查出,这才是正途。 当你看书到某个地方暂时不理解的时候的,暂时放手吧,追求一些行云流水、自然而然的境界吧,只是不要停下前进的脚步,不要被大路旁边的支根末节干扰了你前进的行程,项目,真实的项目,这才是目的,就在不远的前方。 陶渊明读书,叫做“不求甚解”,但他是大诗人。 诸葛亮读书,总是“观其大略”,但他是大政治家。 作研究的,是在确定了方向之后才详细的研究深入的东西。 到了庐山,脉络形状扔在一边,盯着一棵棵小草、一粒粒石子的看,怎么样才能识得“真面目”? 作为应用型的人才,是研究回字的n种写法还是抓紧时间向着主要应用的方向努力,请自己作出抉择。 以项目驱动自己的学习吧,当把握了技术的脉络之后再去补充细节的研究,是正途。 这辈子就不研究其他的方向了吗? 当然要研究!但是在将来合适的时间再说吧。 所以,抓大放小,要事为先! 那么,在JAVA的这些课程中,哪些才是大,才是要事呢?请继续读下去。 JAVA自学之路 五:问题解决之道 既然是学习,就不可能不遇到问题。 既然是自学,就没有方便的和老师探讨的机会。 那么,遇到问题之后,环境配不通,程序调不过,运行不正常,遇见这些恼人的问题的时候,该怎么办呢? 首先我要恭喜你,遇见问题,意味着你又有涨经验的机会了,每解决一个问题,你的JAVA经验值就应该上升几百点,问题遇到的越多,知识提升的越快。 但是总是解决不了也是很恼人的,怎么办呢? 我一般要求我们的学生都是这样来进行的。 当你遇到一个问题的时候: 1:要仔细的观察错误的现象,是的,要仔细 有不少同学的手非常快,在编译一个程序的时候,报了一大堆的错误,扫了一眼之后就开始盯着代码一行一行的找,看清什么错误了吗?没有!有的时候安装软件出问题了,一个对话框弹出来说出错了,马上举手问老师:“不得了了,出错了,出错了”。 “什么错误?” “还没看呢?” 这都是典型的不上心的方法!请记住,学习编程并不是一件很容易的事情,自己首先要重视,要用心才可以。 在开发中,仔细观察出错信息,或者运行不正常的信息,是你要做的第一件事。 读清楚了,才能在以后的步骤中有的放矢,哭了半天,总要知道哭的是谁才成。 这里又分三种情况: A:错误信息读懂了,那么请进入2步:要仔细思考问题会出在哪些环节 B:没读懂,愣是一点没看懂,进入第4步吧:google C:读了个半懂,有些眉目但是不太能确定,第2步和第4步结合着来。 2:要仔细思考问题会出在哪些环节(重要) 当你读懂了一个问题之后,要好好的思考这个问题可能会在哪些环节上出错。 一辆汽车从总成线上下来,车门子关不上! 哪错了?你怎么查? 当然是顺着生产线一站一站的查下来。 程序也是一样的,也是一系列语句完成后产生的结果。 写一个网络程序,总是发现服务器端打印不出接收的数据,有几个环节会出错? 仔细分析这个现象的环节: 客户端产生数据->按“发送”按钮->发送到服务器->服务器接收到后打印 这几个环节都有可能会出错: 有可能客户端根本就没产生数据,有可能发送按钮按下去后根本就没发出去,或者发出去的不是你产生的东西,或者根本就没连接网络,或者发送出去服务器没有接收到,或者接收到之前就打印了等等等等。 学着去这样仔细的分析程序的环节和这些环节可能会产生的问题,你的经验值定然会大幅度快速的提升,这样做很累人,但是一件事情如果做下来一点都不累的话,这个东西还有价值吗? 在网页A输入了一个人的名字,提交到B,首先存储到数据库,然后再读出来,发现乱码!怎么办?当然是分析环节: 客户输入->HTTP发送->B接收->存储到数据库->读出->展现到网页 每个环节都可能出问题,怎么才能知道哪里出的问题?继续往下读。 3:如何定位错误(重要)分析清楚有哪些环节之后,下一步就是定位到底什么环节出错了。 定位有以下三种办法: A 打印输出,比如java的System.out.println(),比如js的alert(),这种办法常用,必须掌握 B Debug,可以参考我们的视频《坦克大战》,详细讲了Eclipse的调试。 C 删掉一部分调试一部分,也就是去掉一部分的功能,做简化,然后调试剩下的功能,JSP和JavaScript常用。 4:如果还不行,google吧 还查不出来?恭喜你,你遇到的错误是值得认真对待的错误,是会影响你学习生涯的错误,问一下google或者百度吧。照着下面的方法查查看。 一般来讲,搜索引擎可以搜到各种知识点,我曾经跟同学们开玩笑说:“学会了怎么google或者baidu,其实没必要跟老师学了,自己全都搞懂了,尚学堂是没必要来的。”当然,只是玩笑,培训的作用有三个: A:系统化知识 B:真实项目锻炼经验 C:少走弯路,节省时间 不知不觉开始做广告了,请原谅,处其位谋其事,总得为尚学堂说点什么:) 言归正传,如何查搜索引擎? 先精后粗,首先先进行尽量精确的查找,比如一个错误,SocketException,你怀疑它是在connect()方法出的问题,那么当然是选这样的关键词java connect SocketException 先中后英,本着以解决问题为主的想法,练习英文还是先放在一边吧,首先应该在中文网页中查询,还不行的话,搜索英文的吧,最近的尚学堂课程中会带大家阅读英文的书籍。有很多东西就像一层窗户纸,远看灰蒙蒙怪唬人的,你壮着胆子一捅,它就破了。阅读英文的书籍就是如此,不是想象中的那么困难:)宁可在沙场上战死,也不能被吓死不是吗:) 信息筛选,搜索出来的结果不见得能够完全匹配,建议大家多阅读前几页的搜索结果,多打开几个网页看看,不过,我的经验是超过3页一般就没意义了,所以超过3页还没有找到合适的答案,或许应该调整一下关键词,或者放粗整个搜索的结果了。 经常的进行知识难点的查询,如果一个问题牵扯的面比较广,就干脆到网上搜索一些相关的专题,比如“java 乱码 mysql” “oracle 创建用户”等等,如果有必要,不要犯懒,勤动手写一些小小的测试程序,来弄明白知识点的细节。这也是涨知识的重要的途径。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值