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)
里的 ‘&’
- 即Java中没有
单继承与多个接口
- 只有一种继承方式,相当于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 ***
}
标记接口
- 没有任何方法的接口称为标记接口。
- 作用
- 建立一个公共的父接口
- 向一个类添加数据类型(instanceof ?)
- 标记接口作用:简单形象的说就是给某个对象打个标(盖个戳),使对象拥有某个或某些特权。
集合,泛型
- 泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。
- 集合框架
$$
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
$$
- 任何类型放到集合接口中,都会隐式强制向上转型成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类的层次
-
原因分类
- 用户输入了非法数据。
- 要打开的文件不存在。
- 网络通信时连接中断,或者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()函数启动线程
- 继承Tread类
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→数据
-