java语言熟悉

Posted by jabin on November 7, 2020

一、 JVM

  1. 核心运行流程、类的加载机制 Java文件->.class文件->类加载阶段(双亲委派,自下而上找上层的类加载器加载,再往下层找,直至找到)->类的生命周期(JVM)
  2. 内存结构 方法区(类信息、静态变量)、堆内存(类对象存储)
  3. GC 栈区、计数器不需要进行GC,与线程的生命周期同步; 方法区和堆内存需要进行GC; 可达性分析(GC ROOTS开始)来解决引用计数的相互调用问题; 标记-删除->标记->压缩->删除
  4. 调优 在做了架构和代码的调优后,才做JVM的调优; 一般是通过调整新生代、老年代、持久代的内存空间大小,以及选择合适的垃圾收集器(串行、并行;CMS)来进行逐步调优; 后续的话还可以考虑sql和操作系统的调优

二、语言的基本语法

  1. 基本结构(类是基本单元)
    public class Hello {
     public static void main(String[] args) {
         System.out.println("Hello World!");
     }
    }
    
  2. 流程控制
  3. 异常处理
    public class Main {
     public static void main(String[] args) {
         try {
             proc1();
         } catch (Exception e) {
             e.printStackTrace();
         }
     }
        
     static void proc1() {
         try {
             proc2();
         } catch (NullPointException e) {
             throw new IllegalArgumentException(e);  // e 原始的异常信息
         }
     }
        
     static void proc2() {
         throw new NullPointException();
     }
    }
    
  4. 反射。通过字符串直接创建类对象,并访问其成员变量或者方法,常用于框架代码的实现,避免出现大量的条件判断
    public class Main {
     public static void main(String[] args) throws Exception {
         // 获取Person的hello方法
         Method h = Person.class.getMethod("hello");
         // 对Student实例调用hello方法
         h.invoke(new Student());
     }
    }
    class Person {
     public void hello() {
         System.out.println("Person:hello");
     }
    }
    class Student extends Person {
     public void hello() {
         System.out.println("Student:hello");
     }
    }
    
  5. 泛型。 代码复用,减少重复的逻辑代码; 类型参数化,在编译期间做类型检查
    // 泛型类、泛型方法
    class DataHolder<T>{
     T item;
     public void setData(T t) {
     	this.item=t;
     }
     public T getData() {
     	return this.item;
     }
     // 泛型方法
     public <E> void PrinterInfo(E e) {
     	System.out.println(e);
     }
    }
    // 通配符
    List<? extends Fruit> flist = new List<Apple>();
    // 泛型擦除
    //虚拟机不知道泛型,都是按Object处理
    //编译器把泛型T当做Object处理,需要转型时,根据T的类型,自动进行安全的类型转换
    
  6. 多线程
    // 1. 基本使用
    public class Main {
     public static void main(String[] args) throws InterruptedException {
         Thread t = new MyThread();
         t.start();  //这里不能直接调用run, 不会启动新的线程
         Thread.sleep(1); // 暂停1毫秒
         t.interrupt(); // 中断t线程
         t.join(); // 等待t线程结束
         System.out.println("end");
     }
    }
    class MyThread extends Thread {
     public void run() {
         int n = 0;
         while (! isInterrupted()) {
             n ++;
             System.out.println(n + " hello!");
         }
     }
    }
    // 2. 线程池
    // 常用的三种使用方式:
    Executors.newCachedThreadPool():无限线程池
    Executors.newFixedThreadPool(nThreads):创建固定大小的线程池
    Executors.newSingleThreadExecutor():创建单个线程的线程池
    // 均是基于ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, 
     TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) ..
    public static ExecutorService newCachedThreadPool() {
     return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                   60L, TimeUnit.SECONDS,
                                   new SynchronousQueue<Runnable>());
    }
    

三、 面向对象编程

3.1. 继承与多态

//多态: 对某个类型的方法的调用,要在执行时才知道是对哪个类型的方法的调用
class Income {
    protected double income;
    public double getTax() {
        return income * 0.1; // 税率10%
    }
}

class Salary extends Income {
    @Override    // 方法名和返回值都相同
    public double getTax() {
        if (income <= 5000) {
            return 0;
        }
        return (income - 5000) * 0.2;
    }
}

class StateCouncilSpecialAllowance extends Income {
    @Override
    public double getTax() {
        return 0;
    }
}

public double totalTax(Income... incomes) {
    double total = 0;
    for (Income income: incomes) {
        total = total + income.getTax();  //并不需要关心具体是哪一项的收入
    }
    return total;
}

3.2. 抽象类与接口

// 接口和抽象类类似, 可以只定义方法,没有实现。 
但是接口更彻底,变量都没有。 
一个类可以实现多个接口,接口间可以继承接口

interface Hello {
    void hello();
}

// 接口继承接口
interface Person extends Hello {   
    void run();
    String getName();
}

// 类实现接口
class Student implements Person {
    private String name;

    public Student(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        System.out.println(this.name + " run");
    }

    @Override
    public String getName() {
        return this.name;
    }
    
    @Override
    public void hello() {
        System.out.println("Hello World!");
    }
}

3.3. 包与模块

//1. 包的作用:避免类名的冲突
// Person.java
package ming;

// 导入mr.jun包的所有class:
import mr.jun.*;

public class Person {
    public void run() {
        Arrays arrays = new Arrays();
    }
}

//2. 模块的作用:声明模块间的依赖; 封装隔离,模块的可读性(requires、exports), 类型的可访问性(public, protect, private)
module com.foo.bar {
    requires org.baz.qux;  // 包含
    exports com.foo.bar.alpha;  // export给其它模块
    exports com.foo.bar.beta;
}

四、Spring & J2EE

  1. Servlet:web服务器的底层封装
  2. IoC容器:组件的创建、基于依赖组装组件、依次销毁组件的解决方案 (中间件/商)

五、I/O & 网络

5.1 单线程

//一个while循环,一个个请求处理,返回后处理下个请求
// 100ms处理一个请求, 1s只能处理10个请求
ServerSocket listener = new ServerSocket(8080);
try {
    while (true) {   // 循环处理链接,和响应请求
        Socket socket = listener.accept(); 
        try {
            handleReq(socket);
        } catch(IOException) {
            e.printStackTrace();
        }
    }
} finally {
    listener.close();
}


public static void handleReq(Socket socket) throws IOException {
    try {
        //socket.getInputStream
        //socket.getOutputStream  out.write("xxxx")
    } finally {
        socket.close();
    }
}

5.2 多线程

//单线程模式下,如果请求处理阻塞在了IO, 那么无法充分利用cpu。利用多线程,可以同时并发处理多个请求
public static class HandleReq extend Tread {
    final Socket socket;
    public HandleReq(Socket socket) {
        this.socket = socket;
    }

    public void run() {
        handleReq(socket)
    }    
}

while(true) {
    Socket socket = listener.accept();
    new HandleReq(socket).start();
}

5.3 线程池

//多线程频繁的创建和销毁, 是对系统资源的巨大浪费。 
//线程池预先创建一批线程,进行复用
// 设计: 线程数有baseNum 和 maxNum, 当ThreadNum>baseNum且<maxNum的时候, 有新的请求需要处理,则创建新的线程进行处理; 
// 当空闲时间大于KeepAliveTime的时候, 删除大于base Num的线程;
// 当线程已经占用满了,还有新的请求的话, 可以加入队列等待, 等队列满了, 再直接拒绝; 也可以直接拒绝, 看场景

ExecutorService executor = Executors.newFixedTreadPool(4);
...
while(true) {
    Socket socket = listener.accept();
    executor.submit(new HandleReq(socket));
}
...

六、简单实践–hello world的web工程

  1. Nginx+Tomcat(servlet)+java
  2. yum install 安装jdk
  3. javac 编译成.class文件
  4. java HelloWorld 运行