1 基础
1.1 进制转换
https://zhuanlan.zhihu.com/p/75291280
1.2 命令提示符
MS-DOS(Microsoft disk operating system)
cd.. 返回上一级
cd \ 返回根路径
dir 查看目录
cls 清屏
exit 退出
javac xxx.java 编译
java xx 解释
jshell 直接在cmd中运行
1.3 JAVA的跨平台特性
-
JVM (java virtual machine): JVM不具备跨平台特性,每个操作系统下都有不同的JVM
-
JRE(java runtime environment): java程序运行的环境,包括JVM和运行时所需要的核心库
-
JDK (java development kit): java程序开发工具包,包括JRE和开发人员使用的工具
想运行,装JRE就可以;想开发,必须装JDK
集成开发环境IDE(integrated development environment): 专门用来提高java软件开发效率的软件,免费的有eclipse,收费的有intellij idea。
1.4 开发步骤
编写->编译->运行
java源程序->编译器->java字节码文件->jvm运行
- 编译器:javac.exe
- 解释器:java.exe
1.5 数据类型
- 整数型:byte short int long (占用1 2 4 8 字节)
- 浮点型:float double(占用4 8 字节)
- 字符型:char(占用2字节)
- 布尔型:boolean(占用1字节)
1.6 ASCII & unicode
ASCII 是美国标准的,unicode是万国码。unicode的0-127跟ASCII是一样的,但是从128开始包括更多字符,甚至包括emoji。
1.7 运行规则
- 不同类型数据运算的时候,结果是数据范围大的。
- 任何数据类型与字符串连接的时候,结果都会是字符串。
- ++num是先加后用,num++是先用后加
- && 是and, ||是or
- int max = a>b? a:b
- str1==str2其实是在比较地址
1.8 快捷键
alt+回车 修复错误
control+y 删除行
ctrl+alt+L 格式的代码
1.9 标准类
- 所有成员变量都private修饰
- 每个成员变量都有一对get set方法
- 有一个无参/又参构造方法
1.10 api
application programming interface: 理解成一个大的字典,里面很多很多方法
2 内存
java的内存划分
- 全部可以划分5个部分:
- stack: 存放的是局部变量(方法的参数或方法内部的变量),特点是有作用域,一旦超出作用域,立刻从栈内存中消失。
- heap:凡是new出来的东西都在堆中,堆内存中的东西都有一个地址(16进制),堆内存中的数据都有默认值。
- method area:存class相关的信息(包括方法的信息)
- native method stack:跟操作系统相关
- pc register(寄存器):与cpu相关
注意:方法运行在栈当中
2.1 一个对象
2.2 两个引用指向同一个对象
2.3 静态static的内存图
内存中先有静态内容,然后才有非静态。静态中不能用this。
2.4 继承多态
3 类
3.1 Scanner类
java.util.Scanner
Scanner sc = new Scanner(System.in);
int num = sc.nextInt();
String str = sc.next();
3.2 Random类
Random r = new Random();
int num = r.nextInt();
int num = r.nextInt(3);// 表示[0,3)
3.3 ArrayList
数组是不可以改变长度的,但是ArrayList可以。
表示范型,也就是说集合中所有的元素都是同一类型的。
ArrayList list = new ArrayList<>();
list.add();
list.get();
list.remove();
list.size();
关于中的E怎么写(把基本类型转化为包装类):
基本类型 | 包装类 |
---|---|
int | Integer |
char | character |
byte | Byte |
long | Long |
… | … |
其他:将基本类型转化为包装类就是将首字母大写
从JDK1.5开始,支持自动装箱(基本类型->包装类)、自动拆箱(包装类->基本类型)
3.4 字符串
字符串的常量池
==其实是在比较地址
双引号写的直接在常量池中,而new出来的不再常量池中(因为是引用对象)
str3是把char[]转化为byte[](不在常量池中)
str1.equals(str2)
str1.equalsIgnoreCase(str2)
3.5 Objects
null是不能调用方法的,会抛出空指针异常。Objects类的equals方法对两个对象比较,但是是可以防止控制能异常
boolean equals = Objects.equals(s1, s2);
3.6 Date类
java.util.Date
表示特定的时刻,精确到毫秒。从1970.1.1 00:00:00(英国格林威治时间)开始算。中国是东八区,会把时间增加8个小时。
System.currentTimeMillis();
Date date = new Date(0L);
System.out.println(date.getTime());
3.7 DateFormat类
DateFormat是一个抽象类,不能直接使用,可以用子类
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Main {
public void demo2() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒");
Date date = new Date();
String d = sdf.format(date);
System.out.println(date);
System.out.println(d);
}
public static void demo3() throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒");
Date date = sdf.parse("2000年06月24日 12时38分55秒");
System.out.println(date);
}
}
3.8 Calendar类
clendar是一个抽象类,无法直接创建对象使用,里面有一个静态方法是getInstance(),该方法返回了calendar类的子类对象
static Calendar getInstance() 使用欧仁时区和语言环境获得一个日历。
public static void demo4() {
Calendar c = Calendar.getInstance();
// set方法
// Calendar.YEAR在Calendar中的静态块
c.set(Calendar.YEAR, 8888);
System.out.println(c.get(Calendar.YEAR));
// get方法
int year = c.get(Calendar.YEAR);
int month = c.get(Calendar.MONTH);
int date = c.get(Calendar.DATE);
// add方法
c.add(Calendar.MONTH, -3);
System.out.println(c.get(Calendar.MONTH));
// getTime方法把日历对象转化为日期对象
Date d = c.getTime();
System.out.println(d);
}
3.9 System类
public static void demo5() {
// currentTimeMillis方法:返回毫秒为单位的时间,用来提高程序效率
long s = System.currentTimeMillis();
// arraycopy方法
int[] src = {1,2,3,4,5};
int[] dest = {6,7,8,9,10};
System.arraycopy(src,0,dest,0,3);
System.out.println(Arrays.toString(src));
System.out.println(Arrays.toString(dest));
}
3.10 StringBuilder类
字符串的底层是一个被final修饰的数组,不能改变,字符串是常量,值在创建之后不能更改。
若进行字符串的相加,内存中就存在多个字符串,占用空间多,效率低下。例如下图,存在5个字符串。
而StringBuilder类(字符串缓冲区)支持可变的字符串。可以提高字符串的操作效率(看成一个长度可以变化的字符串)。它的底层也是一个数组,但是没有final修饰,可以改变长度。
public static void demo6() {
StringBuilder bu1 = new StringBuilder();
System.out.println("bu1: " + bu1);
StringBuilder bu2 = new StringBuilder("abc");
System.out.println("bu2: " + bu2);
// append方法
bu1.append("abc");
System.out.println(bu1);
System.out.println(bu1.append("abc") == bu1); // true 说明调用本身
System.out.println(bu1 == bu2); // false
// 用StringBuilder的构造方法可以把String转化为StringBuilder
String s = "hello";
StringBuilder bu = new StringBuilder(s);
bu.append("world");
System.out.println(bu);
// toString方法:把StringBuilder转化为String
String str = bu.toString();
System.out.println(str);
}
3.11 集合
集合框架
3.11.1 Collection类
java.util.collection接口,是所有单列集合的最顶层的接口,里面定义了所有单列集合共性的方法。任意的单列接都可以使用collectio接口中的方法。
public static void demo1(){
// 创建集合对象,可以使用多态
Collection<String> coll = new ArrayList<>();
System.out.println(coll); // 重写了tostring方法
// add方法
boolean b1 = coll.add("张三");
System.out.println(b1);
System.out.println(coll);
coll.add("李四");
// remove
boolean b2 = coll.remove("赵六");
System.out.println(b1);
System.out.println(coll);
coll.remove("张三");
System.out.println(coll);
// contains 判断是否包含元素
boolean b3 = coll.contains("赵六");
// isEmpty 判断当前集合是否为空
boolean b4 = coll.isEmpty();
// size 判断集合中元素的个数
int size = coll.size();
// toArray 把集合中的元素存储到数据中
Object[] arr = coll.toArray();
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
// clear
coll.clear();
}
3.11.2 Iteration
public static void demo2(){
// 迭代器是一个接口,我们无法直接使用,需要使用Iterator接口实现类对象,获取实现类的方式比较特殊
// iterator接口中有一个方法叫做iterator(), 这个就是返回迭代器的实现类对象
// 但会元素上进行迭代的迭代器
/* 迭代器的使用步骤:
1 使用itrator()获取迭代器的实现类对象,使用iterator接口接收(多态)
2 使用iterator中的方法hasNext判断有没有下一个元素
3 使用iteratir中的hasNext取出集合下一个元素
*/
Collection<String> collection = new ArrayList<>();
collection.add("a");
collection.add("b");
collection.add("c");
collection.add("d");
// 多态接口 实现类对象
Iterator<String> it = collection.iterator();
while (it.hasNext()) {
String e = it.next();
System.out.println(e);
}
System.out.println("-----------------------");
for (Iterator<String> it2 = collection.iterator(); it2.hasNext();) {
String e = it2.next();
System.out.println(e);
}
}
3.11.3 增强for循环
public static void demo3(){
// 增强for循环:底层使用的也是迭代器,使用for循环的格式,简化了迭代器的书写
// Collection<E> extends Iterable <E>: 所有的单列集合都可以使用增强for
// public interface Iterable<T> 实现这个接口允许对象成为foreach语句的目标
// 增强for循环,用来遍历集合和数组
int[] arr = {1,2,3,4,5};
for (int i:arr) {
System.out.println(i);
}
4 继承、多态
4.1 父类和子类
- 继承是多态的前提,如果没有继承就没有多态。
- 区分方法重写(覆盖)跟方法重载:覆盖是方法名称一样,但是参数不一样。
- 子类方法的返回值一定要小于等于父类方法的返回值范围。
- 子类方法的权限要大于等于父类方法的权限修饰符。(public>protected>default>protected)
- java是单继承的,但是可以多级继承
- 不能new一个抽象类,子类必须重写父类中的抽象方法(除非子类也是抽象类)
4.2 接口
接口是多个类的公共规范
接口是一种引用数据类型,最重要的内容是:抽象方法
如何定义接口
public interface 接口名称 {
// 接口内容
}
备注:换成了关键字interface之后,编译生成的字节码文件还是: .java --> .class
如果是java 7,那么接口中可以包含的内容有:
1 常量 [public] [static] [final] 数据类型 常量名称 = 数值;
2 抽象方法 [public] [abstract] 返回值类型 方法名称(参数列表);
要实现类必须覆盖重写接口所有的抽象方法,除非实现类是抽象类
如果是java 8,那么可以额外包含:
3 默认方法 [public] default 返回值类型 方法名称(参数列表){方法体};
默认方法也可以被覆盖重写
4 静态方法 [public] static 返回值类型 方法名称(参数列表){方法体};
只能通过接口名称被调用,不能通过实现类对象调用接口静态方法
如果是java9,那么可以额外包含:
5 私有方法:
普通私有方法:解决多个默认方法之间的重复代码问题 private
静态私有方法:解决多个静态方法之间的重复代码问题 private static
接口使用步骤:
1 接口不能直接使用,必须有一个“实现类”来实现该接口
public class 实现类名称 implements 接口名称 { //方法体 }
2 接口的实现类必须覆盖重写(实现)接口中所有的抽象方法:去掉abstract关键字,加上方法体大括号
3 创建实现类的对象,然后使用。
注意事项:
1 接口没有静态代码块或者构造方法
2 一个类的直接父类是唯一的,但是可以有同时实现多个接口 public class xxx implements xx, xx
3 如果实现类所实现的多个接口中有重名的方法,这时候不用覆盖重写多次,一次就可以
4 如果没有覆盖重写接口中的所有方法,那么这个类必须是一个抽象类
5 如果实现类的多个接口中,存在重名的默认方法,那么一定要覆盖重写
6 一个类中如果直接父类的方法跟接口方法重名冲突,那么父类的优先级更高 继承优先于接口
接口跟类之间的差异
1 类跟类之间是单继承的,直接父类只有一个
2 类跟接口之间是多实现的,一个类可以实现许多接口
3 接口跟接口之间是多继承的:public interface xx extends xx, xx
如果多个父接口的抽象方法重复,没关系
如果多个父接口的默认方法重复,需要重写
4.3 权限修饰符
java中有四种权限修饰符
public > protected > default > private
同一个类 yes yes yes yes
同一个包 yes yes yes no
不同包子类 yes yes no no
不同包非子类 yes no no no4
内部类:如果一个事物内部包含另一个事物。比如:身体和心脏,汽车和发动机
分类:
成员内部类、局部内部类(包含匿名内部类)
注意:
内部类用外部类,随意访问,外部类用内部类,需要内部类对象
如果接口的实现类或者父类的子类只需要使用唯一的一次,那么可以省略掉定义,而改为使用匿名内部类。
如果希望多次创建对象,并且类的内容一样的话,那么必须使用单独定义的实现类
recap:匿名对象是在调用方法的时候使用,只能调用唯一的一次。如果希望多次调用,必须给对象起个名字
如何使用内部类:
1 间接方式:在外部类的方法中,使用内部类,然后main只是调用外部类的方法
2 直接方式:外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称();
4.4 静态代码块
静态代码块:第一次运行本类时,静态代码块运行唯一的一次,用来一次性对静态成员变量赋值。
public class Person {
static {
int i = 0;
}
}
5 包装类
基本数据类型使用起来十分方便,但是没有对应的方法操作这些基本类型数据。
可以使用一个类,把基本类型的数据装起来,在类中定义一些方法,这个类叫做包装类。
我们可以使用类中的方法来操作这些基本类型的数据。
基本类型 | 对应的包装类(位于java.lang包中) |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
基本类型与对应的包装类对象之间,来回转换的过程称为”装箱“与”拆箱“:
-
装箱:从基本类型转换为对应的包装类对象。
-
拆箱:从包装类对象转换为对应的基本类型。
public static void demo7() {
// 装箱
Integer int1 = new Integer(1);// 方法上有横线说明方法过时了
Integer in2 = Integer.valueOf(1);
// 拆箱
int i = int1.intValue();
// 自动拆箱
Integer in = 1;
in += 2;
// 自动装箱
ArrayList<Integer> list = new ArrayList<>();
list.add(1); // 其实是list.add(new Integer(1));
// 自动拆箱
list.get(0);// 其实是list.get(0).intValue();
// 基本类型->字符串
String combine = in + "";
// 字符串 -> 基本类型
int i_parse = Integer.parseInt("20");
}
6 泛型
6.1 泛型概述
泛型是一种未知的数据类型,当我们不知道存什么数据类型的时候可以使用泛型。
也可以看成一个变量,用来接收数据类型。
ArrayList集合在定义的时候,不知道集合中都有存储什么类型的数据,所以使用泛型。
E e: Element 元素
T t: Type类型
在前面学习集合时,我们都知道集合中是可以存放任意对象的,只要把对象存储集合后,那么这时他们都会被提升成Object类型。当我们在取出每一个对象,并且进行相应的操作,这时必须采用类型转换。
public static void demo4() {
// 当我们创建集合对象,不使用泛型
// 好处是可以存储任意类型的数据
// 坏处是不安全可能引发异常
ArrayList list = new ArrayList<>();
list.add(1);
list.add("aa");
// 使用迭代器遍历
Iterator it = list.iterator();
while (it.hasNext()) {
// 取出的元素也是object类型
Object obj = it.next();
System.out.println(obj);
// 想要使用string特有的方法length但是不能使用
// 多态
// 需要向下转型
// 会报错,说不能把int类型转化为string类型
String s = (String) obj;
System.out.println(s.length());
}
// 创建集合对象使用泛型
// 好处是避免了类型转换的麻烦,存储的是什么类型,去除的就是什么类型;
// 好处还有把运行期异常提升到了编译期
// 坏处是泛型是什么类型,就只能存储什么类型的数据
ArrayList<String> ls = new ArrayList<>();
// 报错 list.add(1);
ls.add("bbbb");
// 直接报错 ls.add(1);
// 使用迭代器遍历
Iterator it2 = ls.iterator();
while (it.hasNext()) {
Object obj = it2.next();
System.out.println(obj);
// 想要使用string特有的方法length但是不能使用
// 多态
// 需要向下转型
String s = (String) obj;
System.out.println(s.length());
}
6.2 含有泛型的类
public class RandomClass <E> {
private E name;
public E getName() {
return name;
}
public void setName(E name) {
this.name = name;
}
}
public static void demo1() {
// 如果不写类型的话默认为object类型
RandomClass ls = new RandomClass();
ls.setName("只能是字符串");
// 创建泛型对象
RandomClass<Integer> gc = new RandomClass<>();
gc.setName(1);
}
6.3 含有泛型的方法
public class JustClass {
public <M> void print1(M m) {
System.out.println(m);
}
public static <S> void print2 (S s){
System.out.println(s);
}
}
public static void demo2() {
// 测试含有泛型的方法
JustClass j = new JustClass();
// 调用含有泛型的方法,传递什么类型就是什么类型
j.print1(1);
j.print1("aa");
// 静态方法,不建议创建对象使用
// 静态方法可以通过类名.方法名(参数)使用
j.print2(1);
JustClass.print2("ss");
}
6.4 含有泛型的接口
public interface GeneticInterface <S> {
public abstract void method(S s);
}
public class GenericInterfaceImpl implements GeneticInterface<String> {
/*
含有泛型的接口,使用方式
1 定义接口的实现类,实现接口,指定接口的泛型.
public interface Iterator<E> {
E next();
}
Scanner类实现了Iterator接口,并指定了接口的泛型为String,所以重写的next方法泛型默认就是String
public final class Scanner implements Iterator <String> }
public String next() {}
}
*/
@Override
public void method(String s) {
System.out.println(s);
}
}
public class GenericInterfaceImpl2<I> implements GeneticInterface <I> {
@Override
public void method(I o) {
System.out.println(o);
}
}
public static void demo3() {
GenericInterfaceImpl gi1 = new GenericInterfaceImpl();
gi1.method("字符串");
GenericInterfaceImpl2<String> gi2 = new GenericInterfaceImpl2();
gi1.method("字符串");
}
6.5 泛型通配符
public static void demo4() {
ArrayList<Integer> ls1 = new ArrayList<>();
ls1.add(1);
ls1.add(2);
ArrayList<String> ls2 = new ArrayList<>();
ls2.add("aa");
ls2.add("bb");
printArray(ls1);
printArray(ls2);
// 泛型通配符
ArrayList<?> ls3 = new ArrayList<?>();
}
public static void printArray(ArrayList<?> list) {
// 定义一个方法,可以遍历所有类型的ArrayList集合
// 如果输入参数是ArrayList<Integer> 或者ArrayList<String> list
// 那么不能适用于其他的类型
//如果输入参数是ArrayList<Object> list
// 那么两个都会报错,因为泛型没有继承概念的
Iterator<?> it = list.iterator();
while (it.hasNext()) {
// it.next()取出的元素是Object
Object obj = it.next();
}
}
泛型的上限和下限
之前设置泛型的时候,实际上是可以任意设置的,只要是类就可以设置。但是在JAVA的泛型中可以指定一个泛型的上限和下限。
泛型的上限:
- 格式:
类型名称 <? extends 类 > 对象名称
- 意义:
只能接收该类型及其子类
泛型的下限:
- 格式:
类型名称 <? super 类 > 对象名称
- 意义:
只能接收该类型及其父类型
public static void getElement1(Collection<? extends Number> coll) {
// 必须是Number类或者子类
}
public static void getElement2(Collection<? super Number> coll) {
// 必须是Number类或者父类
}
public static void demo5() {
Collection<Integer> int_coll = new ArrayList<Integer>();
Collection<String> str_col = new ArrayList<String>();
Collection<Number> num_coll = new ArrayList<Number>();
Collection<Object> obj_coll = new ArrayList<Object>();
getElement1(int_coll);
getElement1(str_col);//报错
getElement1(num_coll);
getElement1(obj_coll);//报错
getElement2(int_coll);//报错
getElement2(str_col);//报错
getElement2(num_coll);
getElement2(obj_coll);
}