Java 学习笔记

CNBLOGS @立秋小猪 Github @Maverick618

JAVA 小学期

HelloWorld

public class HelloWorld{  // 主类,必须为public 且 类名与文件名相同
	public static void main(String args[]){ // public static void String 缺一不可
        									// System.lang.String 默认已被 import
		System.out.println("HelloWorld!");  // 有ln则自动换行
    }
}
// 习惯上类名/函数名后加 {


import java.util.* // 调入包util的所有类
import java.util.Scanner // 调入包util内的Scanner

数据类型与从C++到Java

数据类型

  • boolean 和 整型不能相互转换

  • boolean 不能进行数值运算,只能进行逻辑运算

  • 基本类型:整型(byte, short, int, long)、浮点、布尔和字符char(unicode, 0~65535) 放在栈内

    其余类型(数组也是)放入堆内(隐式new),称为引用数据类型

    0.0 默认是double , 0.0f才是float

    float = 0.0会报错

  • 数据类型的默认值

    成员变量有默认值,但局部变量没有默认值。

    数据类型 默认值
    byte 0
    short 0
    int 0
    long 0L
    char '\u0000'
    float 0.0F
    double 0.0D
    boolean false
    reference type null
  • char 可以直接转 int 但 int 转 char 要强制类型转换(char)

  • 用关键字final声明常量

  • String类型:是一个类,可以调用方法(重载了加法运算符: 字符串拼接),而 char 是基本数据类型

  • 求余运算: (-3) % 5

  • 顶层父类:Object

tips:带标签的break, continue

A: for(;;)
	for(;;)
	 for(;;)
	 	break A; // 直接跳出三层循环

if(a = 5){...} //会报错,因为不支持int到Boolean 的隐式类型转换

从C++到Java

引用类型

  • 没有拷贝构造
    • Classname objectname2 = objectname1 不会产生新的对象
    • objectname3 = objectname1 不是对象间的数据赋值,而是引用地址复制,二者实际指向同一对象
    • objectname1 == objectname2 实际上是比较对象的存放地址也即引用是否相等,而非对象数据是否相等
      • 解决办法之一:为需要比较的类写equals函数
  • 参数总是值传递
    • 基本类型发生值复制
    • 引用类型发生地址的赋值,实质就是传引用
      • 即Java中没有void fun(Classname &objectname)里的 ‘&’

单继承与多个接口

  • 只有一种继承方式,相当于C++的公有继承
  • 只能有一个父类
  • 一个类可以实现多个接口

默认虚函数

  • 在Java中,方法缺省情况下是虚的,可以使用final关键字使之声明为非虚方法(与C++相反)。

模板、泛型、反射和多线程

  • java 不支持模板但支持泛型,且支持反射和多线程

输出注释与规范

  • println 自动换行而print不换
  • \t: tab转义字符
  • \n: 换到下一行第一个字符
  • 注释:
    • 单行: //
    • 多行: /* */
    • 文档注释: /** */

作业

/**
* Q1:编写JAVA程序,实现接收用户输入的正整数,输出该数的阶乘
*       要求:限制输入的数据在1-10之间,无效数据进行提示,结束程序
*       输出结果如:4!=1*2*3*4=24
*/

import java.util.Scanner;

public class hw2{
    public static void main(String args[]){
        Scanner input = new Scanner(System.in);
    
        int inputNum = input.nextInt();
        while(inputNum < 0 || inputNum > 11){
            System.out.println("Input Invalid!");
            input.nextInt();
        }
        
        int answer = 1;

        System.out.print(inputNum + "!=");
        for(int i = 1; i <= inputNum; i++){
            System.out.print(i);
            answer *= i;
            if(i < inputNum)
                System.out.print("*");
        }
        System.out.println("=" + answer);
    }
}
/**
* Q2:编写JAVA程序,实现输出1--100之间所有不能被7整除的数,并求和
*    要求:每输出4个数据换行显示
*/
public class hw1{
    public static void main(String[] args){
        int count = 1;
        int sum = 0;
        for (int i = 1; i < 101 ; i++){
            if(i % 7 != 0){
                System.out.print(i + " ");
                sum += i;
                if( count++ % 4 == 0)
                    System.out.println();
            }
        }
        System.out.println("\nSum = " + sum);
    }
}

数组

  • 声明数组: int arr[] = new int[length];int []arr = new int[length];

  • arr.length为数组长度(函数)

  • import java.util.Arrays 数组工具包

  • Arrays.sort(arr) 对arr进行排序(升序)

  • 二维数组: int[] arr[] = new int[a][b];int arr[][] = new int[a][];

    • 非法:··· = new int[][a];

    • 静态初始化:

      int [][]arr = {{1,2,3},{4},{5,6}};
      
    • 动态初始化:

      int [][]arr = new int[3][];
      arr[0] = new int[4];
      arr[1] = new int[3];
      arr[2] = new int[2];
      //赋值
      
  • 地址赋值

    int big[][] = new int[7][7];
    int small[][] = new int[2][2];
    small = big; //可执行
    
  • 对象数组:

    Employee []E = new Employee[3]; 
    // C++中声明对象数组后即可使用对象实例(调用无参构造函数构造的对象是实例)
    E[0].setname("zhang san"); // √
    
    Employee []E = new Employee[3]; 
    // Java中声明对象数组后不能立即使用对象实例
    E[0].setname("zhang san");// ×
    // 需要再次new对象
    for(int i = 0; i < 3 ; i++)
        	E[i] = new Employee();
    // 之后就可以用了
    

String

  • Object.toString() = "" + Object 将类变为字符串

  • 'equals' & '=='

    String a = "Hello";
    String b = "Hello"; //二者均放在静态区,引用地址实际上相同
    String c = new String("Hello");
    String d = new String("Hell0");//在动态区新申请的空间,二者地址不同
    // a == b True
    // b == c false
    // c == d false
    
  • String的不变性

    String str = new String("Hello");
    str.concat(" World"); // 返回一个值为"Hello World"的String
    str.replace('e','a');
    str.toUpper();
    System.out.print(str); //打印结果仍然为"Hello"
    
  • StringBuffer:暂时理解为可变长字符串

基础知识

  • 面向对象(Object Oriented Programming, OOP)

    • 封装
    • 继承
    • 多态
  • 构造函数

    • 一般是Public,但也可以是Private(单例类)
    • 不能有返回值
  • this 引用

    • 代表对象自身的引用。
    • 解决局部与成员变量命名冲突。
    • 在一个构造函数中调用另一个构造函数,但只能在构造函数第一行调用。
    • 返回当前对象的引用。
  • 封装:利用set, get方法可将成员变量的访问权限设为private,并且在set函数里还能进行控制语句。

  • 函数重载:

    • 函数名必须相同
    • 参数(类型或个数)必须不同
  • Java中没法为函数参数设置默认值

  • static:

    • static 类型 变量名;
    • static 成员变量属于类,而不是属于某个具体的对象,即只分配一次内存空间。类中所有对象使用的都是这份内存中的数据。当某个对象修改了其值,也会影响到其他对象。
    • static 成员变量不占用对象的内存,而是在所有对象之外开辟内存,即使不创建对象也可以访问
    • static 变量只能声明成成员变量或静态成员函数里的变量
    • static成员函数
      • 函数的声明和普通成员函数类似,只需在前面加static修饰
      • 函数的类外定义与普通成员函数一样
      • 静态成员函数与普通成员函数的根本区别在于:普通成员函数有 this 指针,可以访问类中的任意成员;而静态成员函数没有 this 指针,只能访问静态成员(包括静态成员变量和静态成员函数)。

抽象类

  • abstract: 表明该类为抽象类
  • 抽象类不能
  • 抽象类里的方法必须要重写

类的继承

  • extends: 单继承,只能有一个父类,避免了菱形结构。
  • 子类继承父类所有的属性和方法,但不继承构造函数(因此构造函数不会被重写)。
  • 一个父类可以有多个子类。
  • super:父类关键字,代表父类对象。super();调用父类默认构造函数,不写也默认调用。
  • 通过super调用父类构造函数,必须写在子类构造函数第一行。
  • 方法重写(override):
    • 子类重写的方法访问权限不能小于父类被重写方法的访问权限。
    • 返回值必须相同或是其子类(如果返回值是一个类的话?)。
  • final:方法不可重写或类不可被继承

多态

  • 两要素

    • 使用父类类型(继承)
    • 方法重写
  • instanceof 运算符:判断引用对象属于的类

    pet instanceof Dog判断当前pet是否是狗狗,是则返回true 否则false

接口

  • 关键字interface, implements

接口与类的区别

  • 接口不可被实例化

  • 实现类必须实现接口的所有方法

  • 实现类可以实现多个接口

  • 接口内的变量必须是public and static (or final)

  • 接口可以多继承

    public interface *** extends ***, ***, ***, ...
    
  • 没有构造函数

特性

  • 接口中的方法会被隐式的指定为 public abstract
  • 接口中的变量会被隐式的指定为 public static final 变量
  • 接口中的方法都是公有的
  • 接口中不能含有静态代码块和静态方法,但抽象类可以。

interface

定义接口

public interface ***{
	public static *** ***; 静态成员变量
    public *** ***; 成员函数
}
  • 可能的二义性错误:接口的静态常量重名
  • 静态常量的访问:接口名.常量

implements

接口实现

class *** implements ***{
	//@override the function in interface ***
}

标记接口

集合,泛型

  • 泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。
  • 集合框架

$$
ArrayList \in List, \quad LinkedList \in List
\ HashSet \in Set, \quad TreeSet \in Set
\ List \in Collection,\quad Set \in Collection
\HashMap \in Map, \quad TreeMap \in Map
$$

img

  • 任何类型放到集合接口中,都会隐式强制向上转型成Object,其方法返回的一般也是Object,给可能需要强制向下转型。
  • 因为上面的特性,集合中允许类型不同的变量。
  • 标记元素类型:ArraryList<***>

迭代器和增强型for

  • 和c++类似

作业/实例

/**
 * 实现一个通讯录管理,能够进行好友分组:家人、朋友、同学,可以根据姓名查找电话号码(姓名有重复),
 * 可以根据电话号码查找姓名,可以根据分组查找(例如查找同学中的王五),请使用泛型集合实现。
 */
package JAVAHOMEWORK;

import java.util.HashMap;
//*******没有使用面向对象编程*******
public class addressBook {
    public static void main(String[] args) {
        // initial
        HashMap<String,String> teleName = new HashMap<String, String>();
        HashMap<String,String> nameGroup = new HashMap<String, String>();
        teleName.put("15000000000","张三");
        nameGroup.put("张三","同学");
        teleName.put("18700000000","李四");
        nameGroup.put("李四","朋友");
        teleName.put("18777770000","李四");
        teleName.put("18234560000","王五");
        nameGroup.put("王五","同学");
        teleName.put("18712345678","狗子");
        nameGroup.put("狗子","家人");
        teleName.put("19500000000","老爸");
        nameGroup.put("老爸","家人");

        System.out.println("同许有:");
        for(String name: nameGroup.keySet()){
            if(nameGroup.get(name).equals("同学"))
                System.out.println(name);
        }

        System.out.println("电话号码为15000000000的是" + teleName.get("15000000000"));
        System.out.println("李四的电话有:");
        for(String tele: teleName.keySet()){
            if(teleName.get(tele).equals("李四"))
                System.out.println(tele);
        }
    }
}

异常

  • Exception类的层次

img

  • 原因分类

    • 用户输入了非法数据。
    • 要打开的文件不存在。
    • 网络通信时连接中断,或者JVM内存溢出。
  • 异常分类

    • 检查性异常:无法预见的异常
    • 运行时异常:通常可避免,编译时可发现的异常。
    • 错误:错误不是异常,脱离程序员控制,难被发现。
  • throw 和 throws

    如果一个方法没有捕获到一个检查性异常,则必须使用throws关键字来声明,throws 关键字放在方法签名后面

    public void ***(***) throws ******

    try{
    	//***
    }catch(异常 a){
    	//todo
    }catch(异常 b){ //若 a ⊆ b,一般异常a放在b前面,如果放在b后,则永远都捕获不到a
    	//todo
    }catch(Exception e){ //大类 不明异常都会被捕获,一般写到最后
    	e.printStackTrace();
    }finally{
        
    }
    

作业/实例

/**
*	数组越界异常测试
*/
package JAVAHOMEWORK;

public class exceptionHW {
    public static void main(String[] args) {
        int[] arr = new int[3];
        try{
            arr[1] = arr[4];
        }catch (ArrayIndexOutOfBoundsException e){
            System.out.println("数组越界异常,数组非法访问异常。");
            e.printStackTrace();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            System.out.println("OVER");
        }
    }
}

I/O

Java流的分类

  • 按流向分类:

    • 输出流:OutputStream(字节)和 Writer(字符)作为基类
    • 输入流:InputStream(字节)和 Reader(字符)作为基类
    • 输入输出是针对计算机内存而言的。
  • 按读取单元分类:

    • 字节流:OutputStream/InputStream
    • 字符流:Writer/Reader

IO异常

  • 嵌套I/O流关闭只需关闭最外层的流,即调用了最外层的close方法,会一层一层关闭流,也成为装饰者模式。

作业

/**
 * 1 读取“readme.txt”中的文本,输出这段英文共有多少句。
 * 并创建一个新的文件“readme2.txt”将加了行号的本文写入新文本中。
 */

package JAVAHOMEWORK;

import java.io.*;
import java.util.ArrayList;

public class FileOp {
    public static void main(String[] args) throws IOException {
        //输入文件
        File file = new File("D:\\Program Code\\WorkPlace\\Java\\readme.txt");
        FileReader fr = new FileReader(file);
        BufferedReader br = new BufferedReader(fr);
        //输出文件
        File readme2 = new File("D:\\Program Code\\WorkPlace\\Java\\readme2.txt");

        if(!readme2.exists())
            readme2.createNewFile();  //创建文件

        FileWriter fw = new FileWriter(readme2);
        BufferedWriter bw = new BufferedWriter(fw);

        String str = null;
        int count = 1;
        while((str = br.readLine()) != null){
            bw.write(count + ": " + str);
            bw.newLine(); // 换行操作
            count++;
        }
        //一定要注意关闭文件,否则写入的东西可能不会保存
        br.close();
        bw.close();

        System.out.println("There are(is) " + --count + " line(s) in the readme.txt file.");
    }
}

多线程

Tread类

  • java.lang.Tread

  • 实现方法:

    • 继承Tread类
      • 重写run()方法
      • 创建对象后调用start()函数启动线程
    • 实现Runnable接口
      • 重写run()方法
      • 创建对象后调用start()函数启动线程

Synchronized 同步

  • 修饰方法
  • 修饰代码块
  • wait 和 notify
    • wait 相当于 p操作
    • notify 相当于 v操作

作业

/**
 * 生产者和消费者问题是线程模型中的经典问题:生产者和消费者在同一时间段内共用同一个存储空间,
 * 生产者向空间里存放数据,而消费者取用数据,如果不加以协调可能会出现以下情况:
 * 存储空间已满,而生产者占用着它,消费者等着生产者让出空间从而去除产品,生产者等着消费者消费产品,从而向空间中添加产品。
 * 互相等待,从而发生死锁。
 * 请完成以下程序,通过线程同步解决生产者/消费者问题。实现产生一个,消费一个的效果。
 */

package JAVAHOMEWORK;

public class ProducerConsumerTest {
    public static void main(String[] args) {
        CubbyHole c = new CubbyHole();//用来存放数据的空间
        Producer p1 = new Producer(c, 1);//生产者
        Consumer c1 = new Consumer(c, 1);//消费者
        p1.start();
        c1.start();
    }
}


class Producer extends Thread {
    private CubbyHole cubbyhole;
    private int number;//生产者编号

    public Producer(CubbyHole c, int number) {
        cubbyhole = c;
        this.number = number;
    }

    public void run() {
        for (int i = 0; i < 10; i++) {
            cubbyhole.put(i);
            System.out.println("生产者 #" + this.number + " put: " + i);
            try {
                sleep((int)(Math.random() * 100));
            } catch (InterruptedException e) { }
        }
    }
}

class Consumer extends Thread {
    private CubbyHole cubbyhole;
    private int number;//消费者编号
    public Consumer(CubbyHole c, int number) {
        cubbyhole = c;
        this.number = number;
    }
    public void run() {
        int value = 0;
        for (int i = 0; i < 10; i++) {
            value = cubbyhole.get();
            System.out.println("消费者 #" + this.number+ " got: " + value);
        }
    }
}

//修改CubbyHole 代码使其能实现产生一个,消费一个
class CubbyHole {
    private int contents;
    private boolean available = false; //有数据为false表示不可执行get()

    public synchronized int get() { //同步方法
        while(!available){
            try {
                wait(); //为空时等待
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
        available = false;
        notify(); //消费者消费完成通知
        return contents;
    }

    public synchronized void put(int value) { //同步方法
        while (available){
            try {
                wait(); //为满时等待
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
        contents = value;
        notify(); //生产者生成完成通知
        available = true;
    }
}

JDBC

  • API: import java.sql or javax.sql

    • DriverManager
    • Connection
    • Statement
    • ResultSet
  • 步骤:由上至下处理

  • 字段标号从1开始

Reflection

DAO(Data Access Object)模式

业务代码和数据访问代码耦合:

  • 可读性差
  • 不利于后期修改和维护
  • 不利于代码复用

松耦合:

  • 将对实体类的所有操作抽取成接口
  • 接口由不同数据库的实现类分别实现

Supplement

  • 软件系统的三层架构

    • 表示层 UI

    • 业务逻辑层 BI

    • 数据访问层 DAO

      用户请求→UI→BI→DAO→BI→UI→数据

posted @ 2021-11-05 10:13  团火  阅读(34)  评论(0编辑  收藏  举报