www, 万维网, 全部信息用连接链接起来html
Java, 静态网页 -> 动态网页java
JDK, Java Development Kit, Java开发工具包git
1995, JDK 1.0 1998, JDK 1.2, Java2 2000, JDK 1.3 2002, JDK 1.4, assert, logging, re 2004, JDK 1.5, 语法增长 2006, JDK 1.6, 普遍, Compiler API(动态编译), 脚本语言支持, WebService支持 2010, Oracle并购SUN 2011, JDK 1.7, 带资源的try, 重抛异常 2014, JDK 1.8, 大改进, lambda表达式 注: 从 JDK 1.5 以后, JDK 1.x 也被称为 JDK x, 如 JDK 1.8 也被叫作 Java 8
JCP, Java Community Process, 社区程序员
JSR, Java Specification Requests, 规范segmentfault
与 C++ 的区别api
编写 Main.java数组
package javanote; public class Main{ public static void main(String args[]) { System.out.println("hello world"); } }
进入新建文件夹 ./javanote
, 而后新建源程序文件 Main.java
, 注意文件名和 public class
后面的类名一致安全
public class Main { // 注意 String[] args 不能省略 public static void main(String[] args){ System.out.println("hello world"); } }
编译, 将会获得 Main.class
目标文件(obj), 字节码 bytecode
, 扩展名 class
.它不是实际机器的最终执行代码bash
# c 表明 compiler $ javac Main.java $ ls Main.class Main.java
运行网络
# 注意不是 java Main.class java Main
经过 JVM
读取并处理 class
文件, 最终转化成 CPU
的指令. JVM for Win/Unix/
模拟了一个操做系统/接口
源程序(.java
后缀) ---javac--- 字节码(bytecode, .class
后缀) ---java--- 在 JVM 上运行
JVM 规定了虚拟的CPU和内存, 包含如下内容: 寄存器集, 类文件结构, 堆栈, 垃圾收集堆, 内存区域
Java 程序不能依赖于垃圾回收的时间或者顺序
GC
是彻底自动的, 不能被强制执行, 程序员最多只能用 System.gc()
来建议执行垃圾回收器回收内存, 可是具体的回收时间, 是不可知的。当对象的引用变量被赋值为 null
, 可能被当成垃圾
GC
自动回收内存, 程序员不须要要没法精确控制回收过程, 也就是说只有 new
, 没有 delete
系统级线程会跟踪存储空间的分配状况
JVM 空闲时, 检查和释放那些能够释放的空间
JRE
JRE = JVM + API(Lib)
JRE
运行程序时的三项主要功能加载代码(class loader), 校验代码(bytecode verifier), 执行代码(runtime interpreter, 所以虚拟机有时候也简单称为一个解释器)
小结: Java运行环境(JRE) 首先由虚拟机 JVM 来装载程序, 而后调用相应的指令来执行
JDK = JRE + Tools
(编译工具 javac
, 打包工具 jar
, 调试工具 jdb
, 执行器 java
, 文档生成器 javadoc
)JRE = JVM + API
开发程序须要 JDK
, 若是只是运行程序则 JRE
够了
类 = 属性(变量, 字段, field) + 行为(函数, 方法, method)
Application 的基本结构
package javanote; publicc class Main{ public static void main(String args[]){ System.out.println("Hello"); } } // 一个文件能够有多个 class, 可是只能有一个 public class, 且与文件同名 // main 方法必须是 public static // main 方法是一个特殊的方法, 它是程序运行的入口 // main 还可用于测试, 直接运行该类的时候会调用 main, 若是是其余类调用这个类的方法, 则不会运行 main // package, 包, 文件路径 // import, 导入其余的类
输入 Scanner, 输出 System.out.print()
package javanote; import java.util.InputMismatchException; import java.util.Scanner; public class Main{ public static void main(String args[]) { try { // 注意, 若是输入的不是整数, 而是小数/字符串, 则会报错. 所以须要try...catch... Scanner in = new Scanner(System.in); int a = in.nextInt(); System.out.println(a); } catch (InputMismatchException e) { // TODO: handle exception } } }
byte, 1 字节
short, 2 字节
int, 4 字节
long, 8 字节
Java 中没有 无符号数
float, 4 字节
double, 8 字节, 浮点数默认是 double
逻辑型
boolean, 1 bit, true/false
Java 中不能够用 if(1)
或 if(0)
, 也不能 if( a = 5 )
boolean 不能比较大小, 不能进行算术运算
2 + true // error true + true // error, 就算换成 boolean a, b; 而后 a + b; 仍然是error true > false // error 1 > false // error true == 6; // error a == b > false // error, ==, != 优先级低, 先作 b > false, 这里也会出错 ( a == b ) > false // error, 相似于 true > false 的error
逻辑运算的 !, &&, ||
只能用于 boolean, 做用到其余类型上面会出错, 这点也和 C
不同
char, 2 字节, 统一使用 Unicode 编码, 跨平台
强制类型转换
double a = 10.3; int b = (int)a; int c = (int) (10/3.0); // (10/3.0)要加括号, 由于(int)是单目运算, 优先级高
数组是一种容器, 全部元素有相同的数据类型, 一旦建立, 则不能改变大小. 数组必须用 new
来分配空间, 数组元素默认初始化.
// 注意方括号的位置 // a和b都是数组, 其中 a 未分配空间, b 分配了 4 个 int 的空间 int[] a, b = new int[4]; // c 是数组, d 是 int 变量 int c[], d;
数组是引用类型, 元素个数能够用变量定义,
// 数组是引用类型 int[] a1 = new int[3]; int b = 10; // 元素个数能够用变量定义, 和c99相似 int[] a2 = new int[b]; // error, 数组是引用类型, 理解为指针, 不能直接给它分配空间, 分配的空间在堆, 必须 new 一块空间而后指向那里 int a3[5];
若是没有对数组进行显式初始化, 则会隐式初始化为 0 或 null, 比 C 安全
// 若是没有对数组进行显式初始化, 则会隐式初始化为 0 或 null int[] a4 = {3, 1, 2}; int[] a5 = new int[]{3, 1, 2}; // 注意, 数组元素会默认初始化, 可是基本数据类型声明后不会默认初始化 int a; a++; // error
与指针的相似之处, 数组变量只是数组的管理者, 而不是拥有者
int[] a = new int[10]; int[] b = a; b[0] = 5; // 此时 a[0] 也变成了 5 // a == b 的结果是 true
复制数组, 不能用 b = a
, 而须要遍历, 除此之外还有 a.clone()
和 System.arraycopy(src, int srcPos, dst, int dstPos, int length)
方法
int[] a = {1, 2, 3, 4}; // 方法1, a.clone() int[] b = a.clone(); int[] c = new int[4]; // 方法2, System.arraycopy() System.arraycopy(a, 0, c, 0, a.length);
每一个数组, 都有一个 .length
属性, 比 C
安全
int[] a = new int[10]; for( int i = 0; i < a.length; i++ ) { } // 也能够这样遍历, 由于数组是个 Iterable 对象 for( int n : a ) { }
二维数组
int[][] a = new int[3][5]; for( int i = 0; i < a.length; i++ ) { for( int j = 0; j < a[0].length; j++ ) { a[i][j] = i * 5 + j; } } // 初始化 int[][] a = { {1, 2, 3, 4}, {1, 2, 3}, } // 注意最后能够多一个逗号
字符串, String, 第一个字母大写, 说明是一个类, 是管理者
String s1 = new String("123"); String s2 = "abc"; // 自动转换 String s3 = s1 + s2 + 12 + 24; // "123abc1224" String s4 = s1 + s2 + (12 + 24); // "123abc36" // 注意 String s5 = null; System.out.println( s5 + "a" ); // nulla
读取输入
next --- 至关于 C++ 中的 cin nextLine --- 至关于 C++ 中的 getline
字符串的操做. 字符串是对象, 对它的全部操做都要经过 .
这个运算符
s.length()
注意要加括号 ()
, 是字符串的 length()
方法, 和数组的 length
属性不同
并且在调用 .length()
时, s
必须指向一块字符串, 不能是未初始化区域(null)
s.charAt(index)
, 注意这是只读操做, read-only 遍历, 注意字符串没法使用 for(char ch : s)
, 由于字符串不是 Iterable
对象
for( int i = 0; i < s.length(); i++ ){ s.charAt(i); }
子串, .substring()
// [n:-1] s.substring(n); // [n, n + len) s.substring(n, len);
内容是否相同 .equals()
if( s.equals("Hello") ) { }
比较大小, .compareTo()
, unicode 编码相减
s1.compareTo(s2); // unicode 编码相减
其余操做
int loc = s.indexOf('a'); s.indexOf('a', loc + 1); s.indexOf("abc"); s.lastIndexOf('a'); s.startsWith(ch); s.endsWith(ch); s.trim(); // 去掉两端空格 s.replace(c1, c2); s.toLowerCase(); s.toUpperCase();
基本数据类型对应的包裹类型
boolean --- Boolean byte --- Byte short --- Short char --- Character int --- Integer long --- Long float --- Float double --- Double
基本数据类型 + 更多方法和字段
int a = Integer.MAX_VALUE; // 2^31 - 1 boolean b = Character.isDigit('a'); // false char c = Character.toLowerCase('A'); // a
数学类, Math
Math.abs() Math.round() Math.random() Math.pow()
<=
形参类型的宽度(大小)好比形参 double 实参 int 是能够的, 可是反过来就不行
因为没有 C 的指针, 也没有 C++ 的引用 &
, 因此通常方法没法实现 swap
, 须要使用数组或者对象
static
不属于某个实例, 而是属于整个类的(属性). static
变量, 至关于该类一个全局变量, 有点像 C
中的 extern
全局变量, 是全部该类的对象实例所共享的. 这样看来, Java
的类是一个树的结构, 根结点中存储着公有信息, 而对象实例是衍生出来的子结点. 也就是说, static
是一个公共的路灯, 只有一盏, 每一个人能够去开关路灯. 可是若是你要去关掉某一户人家里的灯, 就要明确指明他家的门牌号
类就至关于计网中的协议, 好比网络层协议, 规定了每一个数据包应该有什么样的格式. 而对象则是一个个具体的实际的数据包.
类变量, 至关于协议头部的一个字段, 全部这个类的对象实例都有相同的头部信息. 相对的, 成员变量则是数据部分.
static
变量单独划分一块存储空间, 不与具体的对象绑定在一块儿, 该存储空间被类的各个对象所共享.
static
变量值在方法区加载一次, 而 非 static
变量在建立对象时会加载不少次, 每次建立都会拷贝一份
static
方法再内存中有专用的代码段
static
的访问
static
方法能够访问 static
变量 (类变量)
static
方法不能访问无 static
前缀的 普通成员变量
, 不能访问实例变量, 也就是不能使用 this
或 super
, 调用类方法时, 能够经过 `类名.
普通成员函数
能够访问 static 变量
能够经过 类名.类变量
访问类变量, 也能够经过 对象名.类变量
访问类变量. 同理, 调用类方法时能够用 类名.类方法
, 也能够用 类名.类方法
class A{ static int var = 1; public static void main(String args[]){ A.var++; // OK, 类名.类变量 A a = new A(); a.var++; // OK, 对象名.类变量 var++; // OK, 直接访问 } }
final
, 不可改变的, 最终的类前面加上 final
, 表示这个类不能被继承, 有利于 Java 的优化
方法前面加上 final
, 则这个方法不能被子类覆盖 Override
字段前面加上 final
, 能且只能被赋值一次, 而后就不能被改变了. 一般用 static final
表示常量
成员变量的生存期 ---> 该对象的生存期, new 开始, GC 收集
成员变量的做用域 ---> 类的内部
构造方法, 构造函数, new 一个新的对象会调用构造方法, 能够重载 overload (参数表不一样)
初始化顺序
1.new 2.先跳到构造函数(但不进入) 3.调到父类的构造函数(若是有父类) 4.重复3, 一直到最高的父类 5.跳到构造函数外面的 定义初始化 int price = 80, balance = 0; 6.进入构造函数, 完成构造函数里面的语句 7.回到子类, 进行子类的定义初始化, 再完成子类的构造函数 8.把构造出来的对象交给对象实例 vm 管理
若是有构造方法的重载, 并且若是其中一个要调用另外一个构造方法, 那么要借助 this
class Person{ private int age, id; public Person(){ this(1);// 必须放在最开始, 调用另一个构造方法 age = 0; } public Person(int pid){ id = pid; } }
析构函数使用的是 finalize()
方法, 可是通常不用去定义, 交给 JVM 就行了
必须 new, 才会分配具体的空间, 每一个对象的 id 不一样
若是是用另外一个对象对其赋值, 则至关于两我的管理同一个对象实例, 和指针同样
Student s1 = new Student(); Student s2 = s1; s1.setAge(5); // 5 s1.getAge(); // 5 s2.setAge(10); // s2 更新 age s1.getAge(); // 10, s1也会修改, 由于二者指向同一块区域
一个对象实例是内存中的一块区域, 只能经过同类型的引用去访问, 不能随便拿一个指针乱指到一个实例(内存区域)
private
是对类的限制, 而不是对对象的限制
同一个类的内部, 该类的不一样对象实例之间, 能够互相访问private.
可是出了这个类,就没法访问private了。举例以下
class Lower{ private int val = 0; public void test(){ Lower a = new Lower(); Lower b = new Lower(); // 类的内部, 该类的不一样对象实例之间, 能够互相访问private a.val = b.val; } } class Higher{ public void test2(){ Lower l1 = new Lower(); Lower l2 = new Lower(); // error, not visible. 超出了Lower类的内部, private字段就变成了不可见 l1.val = l2.val; } }
private 方法和字段不能继承
public
default
(friendly)既没有前缀 public, 也没有前缀 private, 那么这个成员是 default
那么和它位于同一个包(同一个文件夹)的其余类能够访问.
注意, 不须要显式声明为 default
protected
同一包内的类可见
全部子类可见
父类 public, 子类也必须为 public
父类 protected, 子类 protected, public, private
父类 private 不可以被继承
信息隐蔽, 隐藏类的细节(private), 用户只能经过受保护的接口访问某个类
class Person{ private int age; public int getAge(){ return age; } }
继承的例子 (extends
)
class Person{ int age; String name; } // 注意关键字 extends 表示继承(扩展) class Student extends Person{ String school; } // 若是没有 extends xxx, 那么默认为 extends java.lang.Object 的子类 // 所以全部的类都是直接或间接地继承了 Object
JDK 1.5 以后引入了 @Override
, 表示覆盖 (修改) 了父类的方法.
能够当作注释, 同时编译器会帮你检查, 若是父类中没有相同的 方法名
+ 参数表
, 就会报错
父类方法 func()
被覆盖后, 仍然能够用 super.func()
调用父类的方法
方法名相同, 参数表不一样. 重载是新的方法.
构造方法是不能继承的, 由于构造方法是和类名同名的
子类构造的过程当中须要用 super
来调用父类的构造方法
例题, 问如下代码的结果是什么
class Person { String name = ""; public Person(String n) { name = n; } } class Employee extends Person { String empID = ""; public Employee(String id) { empID = id; } } public class Test { public static void main(String args[]) { Employee e = new Employee("123"); System.out.println(e.empID); } }
结果是编译报错, 由于父类 Person
没有无参数的构造函数, 解决方法
// 方法1, 给父类 Person 增长无参数的构造函数(子类会默认调用) class Person { public Person() {} } // 方法2, 子类构造函数中用 super 显式调用父类已有的构造函数 class Employee extends Person { public Employee(String id) { super("Bob");// 注意 super 必须放在第一句 empID = id; } }
子类对象和父类对象的转换
Student
是一个 Person
(子类对象能够被视为父类对象)
Person
不必定是 Student
, 具体状况看下面的例子
// 假设 Student extends Person Person p1 = new Student(); // 编译正常, 运行正常 Student s1 = (Student) p1; // 编译正常, 运行正常 Person p2 = new Person(); Student s2 = (Student) p2; s2.xxx(); // 【编译正常, 运行错误】
abstract
) 和接口(interface
)abstract
, 抽象
abstract class Person{ // 含有抽象方法的类必须声明为抽象类 // 注意抽象方法是分号;结尾, 而不是花括号 // 也就是说抽象方法只有声明, 没有实现 abstract void speak(); void eat() { // 抽象类能够有非抽象方法 } } class Student extends Person{ @Override void speak() { // 子类 extends 父类后 // 要么仍然保持 abstract 方法 // 要么 Override 覆盖(这里也能够称为实现)父类的 abstract 方法 } } public class Main{ public static void main(String args[]) { // 错误, 抽象类不能被实例化 `instantiate` // Person p1 = new Person(); // 可是抽象类能够用于定义变量 Person p2 = new Student(); } }
interface
, 接口, 约定某种特征
interface 是纯抽象类, 全部方法都是 abstract
, 也就是 C++
中的纯虚类
全部的成员变量都是 public static final
(至关于 const
常量)
static
代表它属于这个类而不是某个具体对象, final
代表它一旦初始化就不会被改变, 是一个编译时刻已经肯定的常量
// 注意直接用 interface 修饰, 不须要 class interface Flyable{ // 接口类的全部方法都是 `public abstract`, 不须要显式写出 // public abstract void fly(); void fly(); } interface Runnable{ // 若是使用 static 修饰符, 则代表这是默认实现, 不须要每次 implements 都去实现 run // java8 以上的新特性, 可是这就又回到父子继承了 static void run() { } } // implements 多个接口用逗号分隔 class Superman extends Person implements Flyable, Runnable{ @Override public void fly() { } } class Airplane implements Flyable{ @Override public void fly() { } }
接口能够继承接口,可是不能继承(extends)类
接口不能实现接口,只有类可以实现(implements)接口. 一个类能够实现(implements)多个接口(多继承), 也就是说, 经过接口能够实现不相关类的相同行为(fly()
), 而不须要考虑层次关系(``)
类 class
, 2.接口 interface
, 3.数组
case1, 编译时多态, 静态
重载 Overload, 多个方法同名但参数表不一样
p.say(); p.say("Hi");
case2, 运行时多态, 动态
覆盖 Override, 子类方法覆盖父类同名方法. 动态绑定 dynamic binding, 也称为虚方法调用, 运行的时候根据实例对象来调用方法, 举例以下
class Person{ void say(){ System.out.println("hi"); } } class Student extends Person{ @Override void say(){ System.out.println("hi, I'm a student"); } } public class Main{ static void func(Person p){ p.say(); } public static void main(String args[]) { Person p = new Person(); Student s = new Student(); Person ps = new Student(); // func() 的参数表说明了咱们须要一个 Person 对象 func(p); // 可是咱们也能够把 Student 传进去 // 这时候不会报错, 并且会正确调用子类的 say() func(s); // 更进一步, 若是咱们用向上造型, func() 能接受 ps, 且会正确调用子类的 say() func(ps); } }
能够被 Override
的方法均可以称做虚方法, 虚方法不须要特殊的声明
非 static、final、private
修饰的全部方法都是虚方法
父类 obj = new 子类();
子类的对象, 能够被当成父类的对象来使用, 能够赋值给父类的变量, 能够传递给须要父类对象的函数, 若是一个容器存放的是父类对象, 那么子类对象也能够放进去
// 为何? 由于继承了父类, 对外的接口是同样的
子类的对象能够赋值给父类的变量. 注意, Java
不存在 对象
对 对象
的赋值, 而是让两个管理员共同管理一个对象. 类比指针, 不是复制内容, 而是给地址.
container
, 是放东西的东西, 数组能够看作一种容器, 可是数组的长度一旦肯定就没法改变, 而容器通常能够改变其容量基本数据类型数组
与 对象数组
// 基本数据类型数组, 每个元素都是基本数据类型 int[] ai = new int[32]; // 每一个元素被初始化为0 // String[] 数组, 对象数组, 每个元素只是一个管理者, 一个指针, 而不是保存实际内容 // 元素初始化为 null, 实际内容还不存在, 须要 for 循环去建立和赋值(指向) String[] as = new String[10];
ArrayList
容器类
// import java.util.ArrayList; ArrayList<String> noteList = new ArrayList<String>(); // 用法参考 https://www.w3schools.com/java/java_arraylist.asp noteList.add( Integer.toString(0) ); noteList.add( "1" ); noteList.get(0); noteList.set(0, "00"); // import java.util.Collections; // 排序 Collections.sort(noteList); noteList.remove(0); noteList.clear(); // 若是要构建一个数组, 能够调用 toArray() 方法 int size = noteList.size(); String[] mylist = new String[size]; noteList.toArray(mylist);
Set
去重
HashSet<String> s = new HashSet<String>(); s.add("first"); s.add("second"); s.add("first"); sysout(s); // [first, second] s.contains("1"); // false
Map
键值映射关系
/* 映射关系 1 penny 5 nickel 10 dime 25 quarter 50 half-dollar */ HashMap<Integer, String> dollarMap = new HashMap<Integer, String>(); dollarMap.put(1, "penny"); // 注意其余容器都是 add, map 是 put dollarMap.put(5, "nickel"); dollarMap.put(10, "dime"); dollarMap.put(25, "quarter"); dollarMap.put(50, "half"); dollarMap.replace(50, "half-dollar"); dollarMap.containsKey(30); // false dollarMap.get(30); // null dollarMap.get(50);