《Java程序设计》复习整理
★掌握java应用程序和java小应用程序在jdk环境下的开发步骤
命名规范:
包全小写experiment。
类,接口,枚举 类型每个单词开头大写NeedHelp(帕斯卡命名法)。
常量全部大写CNT。
变量,方法,属性,成员 除了第一个单词每个单词首字母大写mTeacher(驼峰命名法)。
★java的基本顺序结构
1 各种基本类型名称(关键字)、占用内存空间,缺省值等
2 各种基本类型的常量和变量:如何定义
3 Java运算符
4 类型转换
5 字符串 (定义、比较、String提供的常用方法等)
6 基本的输入输出(控制台输出和键盘输入)
7 流程控制
8 一维数组
★对象与类
OPP达到软件工程三个目标:重用性,灵活性,扩展性
1 对象
类的实例,是个体,动态的
2 类的定义
创建对象的模板,是一种数据类型,静态的
3 类的构造方法
修饰符 (static)(final) class 类名(extends)(implements){
类体(成员变量,属性,方法)
}
修饰符:
public protected default private表示访问权限
-
Public,所有类和包(可以跨包),在不同包下使用导入包就行
-
Protected本包和所以子类(子类可以跨包)
-
default,只在本包使用
-
Private只在本类使用,一般private用在方法构建上,没见过用来构建类的
构造方法默认只有一个无参构造,当自定义有参构造,系统自带的无参构造不可使用,除非自定义无参构造
4 this、final、static 用法
1.This是对当前对象的引用
2.final修饰的变量必须初始化,且一旦初始化后不能再被修改
3.静态方法,静态变量用static修饰
属于类级别而不是对象级别。可以直接通过类名调用,也可以通过对象名进行调用。该类的所有对象都可以对其做修改
5 方法参数java的值传递
所有参数传递都是值传递
6 方法的重载:
一个类中方法名称相同,但参数的个数,类型或顺序不同,与返回值无关(重载对返回值没有要求,可以相同,也可以不同)
注意:1.方法名一样,参数类型一样,只有返回值不一样,这个不构成重载
2.只有形参的名称不同,不构成方法重载。
3.与普通方法一样,构造函数也可以重载。
(两个方法具有相同的名称,相同的参数并且具有不同的返回类型(方法类型不同,不过这个情况不会出现,同一个 方法名不同类型会 冲突),那么这不是有效的方法重载示例)
总结;方法名相同,方法类型相同,参数列表不完全相同
不要求返回值相同
7 对象实例化
类名 对象名=new 类目();
8 包的创建、import关键字
9 属性
针对成员变量的getter和setter方法
★继承
作用:复用类的方法和属性,在此基础上还可以添加新的方法和属性。
1 类继承的实现
修饰词 class 子类名 extends 父类名{
类体
}
只能单继承,一个父类
没有extends,默认继承java.lang.Object
子类继承父类的全部成员和属性
2 super、this关键字
Super显式调用父类构造方法。super.属性或方法,显式调用父类的属性和方法。
调用父类构造方法super语句必须放在子类构造函数的第一行,super可以重载。
如果没有显示调用父类的构造方法,则默认调用父类默认构造方法(无参构造方法,前提父类有无参构造,自定义无参构造或者系统默认无参构造)。
- super关键字代表的是父类空间的引用,this对当前对象的引用
- super关键字是调用父类的构造函数,this关键字是调用本类的构造函数
3 方法的覆盖(重写),区别于方法重载
方法覆盖是在子类中重写父类的方法,方法重载是同一个类中有多个方法,方法具有相同类型名不同参数列表(与返回值无关)
4 多态性
多态性是面向对象编程的又一个重要特征,它是指在父类中定义的属性和方法被子类继承之后,可以具有不同的数据类型或表现出不同的行为,这使得同一个属性或方法在父类及其各个子类中具有不同的含义
覆盖(方法覆盖)和继承(子类对象可以视为父类类型)
父类名 对象名=new 子类名()
instanceof
instanceof是Java的一个保留关键字,左边是对象,右边是类,返回类型是Boolean类型。它的具体作用是测试左边的对象是否是右边类或者该类的子类创建的实例对象,是,则返回true,否则返回false。
5 final 关键字修饰类和方法的用法、涵义
当用final修饰一个类时,表明这个类不能被继承。final类中的成员变量可以根据需要设为final,final类中的所有成员方法都会被隐式地指定为final方法。
final修饰一个方法表示该方法不能被任何派生的子类覆盖。
Final修饰变量相当于定义一个常量
总结:不能继承,不能覆盖,不能修改
修饰符 (static)(final) class 类名(extends)(implements)
6 强制类型转换
在Java中由于继承和向上转型,子类可以非常自然地转换成父类,但是父类转换成子类则需要强制转换。
Father father=new Son()表示先构造一个Son类型对象,然后用一个Father类型变量引用它:在这里Son 对象实例被向上转型为father了,但是请注意这个father对象实例在内存中的本质还是Son类型的,只是new Son()的引用类型被设置为Father而已
使用功能较弱的类型引用功能较强的对象,这是可行的。但是将功能较强的类型引用功能较弱的对象时,就不一定可行了,有一点的条件。
Son son = (Son) father;
这条语句是可行的,变量son引用了变量father的地址(两个变量的相互引用), 而father引用的对象地址是new Son()创建的Son类型的对象,这里只是将引用的类型强转为Son,而其本质引用地址所存储的对象就是Son类型的,所以是可行的。所以这里的Son类型变量引用Father类型对象通过强制类型转换可行。
7 抽象类和抽象方法
抽象类产生的原因:在父类中无法确定子类的某一行为但是肯定子类拥有某一行为可以定义抽象方法。
public abstract 类型 方法名(); (abstract不能省略)
抽象方法没有方法体。抽象方法必须在子类中被重写。
外部类写法:
内部类写法:
内部类写法:
(这三个类是分开写的)
含有抽象方法的类一定是抽象类。
抽象类不能实例化,必须被继承再实现。
8成员的各种访问控制权限
Public protect default private,上面有写
9 Object 提供的常用方法equals()、toString()、hashCode()
Object是所有类的父类。
equals():
实现过程:先比较地址,通过返回true ,不通过再判断括号内的对象是不是string类型(string的equalsy已经被重写过了),如果不是string类型返回false,如果是string类型继续判断两个字符串的长度,如果长度不相等返回false,如果长度相等继续对字符串里面的每个字母进行对比,如果有一个字母不相同就返回false直到对比到最后一个字母都没有问题,然后返回true
用来比较两个对象的内容是否相等
对于自定义类型要想让equalss实现内容比较需要重写
public boolean equals(Object o) {
...
}
区别于==
==:如果比较的对象是基本数据类型,则比较的是数值是否相等;
如果比较的是引用(需要分配内存new)数据类型,则比较的是对象的地址值是否相等。
toString:返回表示对象值的字符串
public String toString() {
return...;
}
hashCode:散列码
public int hashCode() {
return Objects.hash(...);
}
10 泛型数组列表ArrayList
非泛型ArrayList arr=new ArrayList();什么类型对象都可以放入
泛型ArrayList< Object > arr=new ArrayList< Object >();对象有固定类型要求
动态数组只能存放对象不能存放值
11 对象包装器
有时,需要将int这样的基本类型转换为对象。所有的基本类型都有一个与之对应的类,例如,Integer类对应基本类型int。
通常,这些类称为包装器(wrapper)。这些类名字很明显:Integer、Long、Float、Double、Short、Byte、Character、Void、和Boolen(前6个类派生于公共的超类Number)
对象包装器类是不可变的,即一旦构造了包装器,就不能改变包装在其中的值了。同时,对象包装器类还是final,因此不能定义他们的子类。
例如,Interger a = 10 ; 10就不可以改变了,当下一个指令为a=11;时,a的值变为11,但是只是创造了一个新对象,a指向了值为11的这个对象,而没有改变值为10的那个对象。
自动装箱:
ArrayList list = new ArrayList<>();
list.add(3);将自动地变成list.add(Interger.valueOf(3));
自动拆箱:
int n = list.get(i)翻译成:int n = list.get(i).intValue();
12 枚举类
定义枚举类
修饰符 enum 枚举类名{
}
例子一:
enum Day {
MONDAY, TUESDAY, WEDNESDAY,
THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
//两个类是分开的
public class Test {
public static void main(String[] args) {
Day day= Day.MONDAY;
System.out.println(day);
//MONDAY
}
}
例子二:
public enum CurrencyUnit {
DOLLAR("美元"),
POUND("英镑"),
EURO("欧元"),
RMB("人名币"),
YEN("日元");
//美元、英镑、欧元、人民币、日元
private final String explanation;
public CurrencyUnit(String explanation) {
this.explanation = explanation;
}
}
System.out.println(CountryUnit.CHINA.toString());
//name=CHINA, explanation=中国, currency=RMB
枚举类的比较用==就行
13 反射机制(不考)
★接口与内部类
接口是一种特殊的抽象类,只包含常量和方法的定义,没有方法的实现。
1 接口的定义与实现
定义接口:public interface 接口名{}
实现接口:修饰符 class 类名 implements 接口名{}
接口是抽象的,不可以实例化.
(抽象类也不能实例化,都需要子类继承/实现通过多态的方式进行实例化)
接口是对行为进行抽象。
如果想对接口实现实例化可以参考多态的方式进行实例化:(通过实现类对接口实例化这叫接口多态)
2 接口的语法特性
语法:
- 实现接口用implements,接口可以多实现
- 接口只能单继承
- 接口内部默认常量,只能是常量
- 接口里面不能有非抽象方法,可以有抽象方法
- 接口不能实例化,除非多态接口
特性:
- 可以实现不相关类的相同行为,不需要考虑类的层次
3 接口与继承的区别
- 类与类的关系是继承,单继承
- 类与接口的关系是实现,单实现,多实现
- 接口与接口是关系是继承,单继承,多继承
4 常量接口的定义、使用(?接口中的常量吧)
- 接口当中的常量,可以省略public static final ,注意:不写也照样表达这个意思。(接口内部默认是常量 )
- 接口当中的常量,必须进行赋值:不能不赋值。
- 接口中常量的名称,使用完全大写的字母,用下划线进行分隔。(推荐这种命名规则)
5 接口与抽象类的区别
相同:
- 都不能实例化
- 都包含抽象方法
不同:
- 接口只能有抽象方法和常量,所以接口中定义默认都是抽象方法或者常量
- 抽象类中常量,变量,构造方法,抽象类,非抽象方法都可以有(一定有抽象方法)
- 抽象类包括属性和行为,接口主要行为,这是设计理念的不同
6 Lambda表达式
Lambda 允许把函数作为一个方法的参数
public class Java8Tester {
public static void main(String args[]){
Java8Tester tester = new Java8Tester();
// 定义函数
// 类型声明
MathOperation addition = (int a, int b) -> a + b;
// 不用类型声明
MathOperation subtraction = (a, b) -> a - b;
// 大括号中的返回语句
MathOperation multiplication = (int a, int b) -> { return a * b; };
// 没有大括号及返回语句
MathOperation division = (int a, int b) -> a / b;
System.out.println("10 + 5 = " + tester.operate(10, 5, addition));
System.out.println("10 - 5 = " + tester.operate(10, 5, subtraction));
System.out.println("10 x 5 = " + tester.operate(10, 5, multiplication));
System.out.println("10 / 5 = " + tester.operate(10, 5, division));
// 不用括号
GreetingService greetService1 = message ->System.out.println("Hello " + message);
// 用括号
GreetingService greetService2 = (message) ->
System.out.println("Hello " + message);
greetService1.sayMessage("Runoob");
greetService2.sayMessage("Google");
}
interface MathOperation {
int operation(int a, int b);
}
interface GreetingService {
void sayMessage(String message);
}
private int operate(int a, int b, MathOperation mathOperation){
return mathOperation.operation(a, b);
}
}
//转载自菜鸟教程
7 内部类
内部类对象可以访问外部类对象中所有访问权限的字段,同时,外部类对象也可以通过内部类的对象引用来访问内部类中定义的所有访问权限的字段
- 普通内部类,普通内部类对象依赖外部类对象而存在,即在创建一个普通内部类对象时首先需要创建其外部类对象
(测试类和Outer类不在一块,分开的)
public class Demo {
public static void main(String[] args) {
//创建内部类对象,并调用方法
// Inner oi=new Inner();报错
Outer.Inner oi=new Outer().new Inner();
oi.show();
}
}
- 成员内部类,和成员变量,成员方法处于同一个位置
- 局部内部类,和局部变量,局部方法处于同一个位置
- (这个最常用)匿名内部类,说是一个类,本质上是一个对象
public interface Inner {
public abstract void show();
}
public class Outer {
private int num=20;
public void method(){
new Inner(){
@Override
public void show() {
System.out.println("匿名内部类");
}
};
}
}
static修饰的静态内部类不能访问其外部类对象的成员
8 Comparator和 Cloneable(后面这个不考,但我觉得我可以找个时间补充到笔记里)
两种比较方法:
1.内部交换器–Comparable 接口。直接对需要比较的类进行比较接口的实现,后面直接 Collections.sort(arrayList);就行
public abstract class Geometry implements Comparable<Geometry> {
/*.其他代码,包括构造方法..*/
public int compareTo(Geometry o) {//重写比较器的compareTo方法
// 1.return (int)(this.getSurface-o.getSurfacr) 会有小数点丢失的情况
// 2.double t=this.getSurface()-o.getSurface();
// if(t>0) return 1;
// else if(t<0) return -1;0只能放在最后的else ,如果放在前面,-0,0不一致,原因是浮点数的存储
// else return 0
//最好的办法:3.
return Double.compare(this.getSurface(), o.getSurface());
//o表示心中的数,算右边的书,this表示原来的数,算左边的
//返回值为1, 表示左边的数比右边的数大,左右的数进行交换。
//所以左边的数都比右边的数小,升序
}
}
2.外部比较器。 另外写一个比较类,然后通过比较方法
private static void sortBySalary(ArrayList staffs) {
Collections.sort(staffs, new SalaryComparator());
}
public class SalaryComparator implements Comparator<Staff>{
@Override
//降序
public int compare(Staff first, Staff second) {
return second.getSalary()-first.getSalary();
//返回值为1, 表示右边的数比左边的数大的时候,左右的数进行交换。
//所以右边的数比左边的小,所以是降序。
}
}
**3.匿名内部类写法/自定义Comparator方法 **
Collections.sort(array, new Comparator<Staff>() {
@Override
public int compare(Staff o1, Staff o2) {
return o1.getSalary()-o2.getSalary();
}
});
4.Lambda写法
Collections.sort(array,(p,q)->{
//降序,sort函数默认升序
return q.getSalary()-p.getSalary();
});
★图形用户界面设计
1 组件分类
2 能建立简单的窗口程序
3 窗口程序中,组件绘制怎么完成
4 事件处理模型 事件处理的几种基本方式
5 常见的容器布局管理器,使用方式
6 熟悉基本的控制组件对应的类,如何使用
★异常处理
1 异常的概念
程序执行过程中遇到的错误
2 Java中常见的异常
空指针异常
索引越界异常
类型转换异常
出现异常的运算条件
3 异常的基本语法
try必须搭配一个catch
finally里代码无论有无异常都会执行
多个catch放置时,不能把父类异常放在子类异常前
try{
…
}catch(ExceptionType e){
…
}finally{
…
}
最常用的就是
try{
…
}catch(Exception e){
e.printStackTrace();
}finally{
…
}
4 异常的分类
均派生于Throwable类,有两种,一种错误(不可恢复),一种异常(可恢复可处理)
- Error运行时出现系统内部错误
- RunTimeException:运行时期异常,编译的时候是可以通过的。(数组越界,空指针,类型转换)
上面这两种都是未检查异常,其他都是已检查异常
- 非RunTimeException:编译时就得处理否则不能编译。
5 捕获异常 try… catch…finally
6 异常声明 throws
throw起的作用只是抛出异常。也就是当前方法不能处理这个异常
在方法声明时在其首部声明所以可能抛出的异常
public void method(String name) throws IOException{
}
7 异常抛出 throw
throw new Exception();
public static class ScoreException extends Exception{
public ScoreException(String message){
super(message);
// 传递异常信息
}
}
public static class Teacher {
public void checkScore(int score)throws ScoreException {
if(score<0||score>100)
throw new ScoreException("分数应处于0~100之间");
else System.out.println("满足分数要求");
}
}
★集合与泛型
Collection, Set, List, Map
ArrayList, HashMap, Iterator, ListIterator
Collection,List,Set,Map都是接口
1 List接口 对应的实现类 ArrayList
列表,按所以位置排序,可以有重复对象
ArrayList动态数组
List<Object> arr = new ArrayList<Object>();
2 Map接口 对应的实现类HashMap
每一个元素保护一对键和值,没有重复的键对象。值可以重复。
HashMap通过哈希算法来获取对象,存取效率高
Map<Type,Type> hm = new HashMap<Type,Type>()
3 Iterator, ListIterator
iterator迭代器,是一个接口
使用集合中的方法 iterator()获取迭代器的实现类对象,使用Iterator接口接收(多态)iterator接口的实现类对象。
hasNext():
判断当前指向元素是否为空
next():
返回当前指向元素
Iterator<String> iterator=list.iterator();
// [hello, world, java, hello]
while(iterator.hasNext()){
String str=iterator.next();
System.out.println(str);
}
}
}
remove
while (iterator.hasNext()){
String str=iterator.next();
if(str.equals("world"))
iterator.remove();
}
ListIterator是List集合特有的迭代器,继承Iterator,也有hasNext()和next()方法
ListIterator有一个反向便利的功能
★流与文件
最后都要记得.close
1 InputStream
FileInputStream fis=new FileInputStream("temp.txt");
int read():读取单个一个字节的数据,如果到达文件末尾返回-1。
int read(byte[] b);返回的是实际读取的字节大小,当读取到结尾没有任何元素可以读取了返回的也是-1.
while((len=fis.read(bytes))!=-1){
fos.write(bytes,0,len);
}
2 OutputStream
覆盖写入
FileOutputStream fos=new FileOutputStream("D:\\eight_exeperiment\\temp.txt");
// 写入单个字节(ascill码)
fos.write(97);
fos.write(57);
fos.write(55);
// a97
// 一次写一个字节数组数据
byte[] bytes={97,57,55};
fos.write(bytes);
// 一次写一个字节数组的部分数据
fos.write(bytes,1,1);
// 9
最终结果:a97a979
追加写入,在文件名后面加个true
FileOutputStream fos=new FileOutputStream("D:\\eight_experiment\\temp.txt",true);
fos.write("hello".getBytes());
最终结果:hello
Reader和Writer这两个抽象类主要用来读写字符流
3 Reader
Reader为抽象类,需利用其子类创建对象(FileReader)
Reader r = new FileReader(文件路径);
读取方法:
第一种方式:读取单个字符 int read() 读取单个字符,一个个的读,返回值是字符本身(ACSII);当read()返回-1证明文件内容已经读完
第二种方式:int read(char[ ] arr) 将字符读入数组,返回读取的字符数量,若已经读完,则返回-1,将从文件读取的数据存入char数组中,一般char数组的长度通常定义为1024
4 Writer
Writer同Reader相似,同为抽象类,因此同样可以借用子类创建对象
Writer w = new FileWriter(文件路径);
进行写的操作的时候,即w.write()方法时,用完该方法最好刷新一下流,即w.flush(),否则流中的数据会有损失;关流操作的close()方法会在执行关流操作之前执行一次flush()方法。
区分,字节流FileInputStream用的数组是bytes类型,字符流Reader用的的char类型
Reader和Writer借鉴原文链接:https://blog.csdn.net/weixin_42386014/article/details/81543567
★多线程
1 线程的概念
进程:是正在运行的程序
线程:是进程中的单个顺序控制流,是一条执行路径
2 实现线程的2种方式
1.自定义类继承Thread类,重写run方法
- 定义一个类MyThread继承Thread类
- 在MyThread类中重写run方法
(为什么要重写run方法?因为run是用来封装被线程执行的代码,自定义线程里不止由被线程执行的代码) - 创建MyThread类的对象
- 启动线程
public class MyThread extends Thread{
@Override
public void run() {
/*...*/
}
}
启动线程
MyThread myThread = new MyThread();
myThread.start();
2.实现Runnable接口 (主要使用第二种)
- 定义一个类MyRunnable实现Runnable接口
- 在MyRunnable类中重写run()方法
- 创建MyRunnable类的对象
- 创建Thread类的对象,把MyRunnable对象作为构造方法的参数
- 启动线程
Thread receiveThread=new Thread(new ReceiveMailRunnable());
public class ReceiveMailRunnable implements Runnable{
@Override
public void run() {
/*...*/
}
}
3 线程的几个重要方法
- start()
- join()方法的实现原理是不停检查join线程是否存活,如果join线程存活则让非join线程永远等待。直到所有的join线程完成后,线程的其他方法会被调用。(所有join线程同时进行)
try {
// 调用多个子线程的join()方法,让主线程等待它们执行完再让主线程执行
receiveThread1.join();
receiveThread2.join();
receiveThread3.join();
sendThread1.join();
sendThread2.join();
sendThread3.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("foxmail任务结束");
//所以线程结束才执行最后的println
- void setName(String name) 返回此线程的名称
- static void sleep(long millis) 使当前正在执行的线程停留 (暂停执行)指定的毫秒数
4 同步代码块synchronized关键字,wait()和notify()
当一个线程进行同步代码块执行区域,只有还在区域内,其他线程就不能进入该区域
public class SumWorker implements Runnable{
private static Object obj=new Object();
private static long sum=0;
private long beg,end;
@Override
public void run() {
for (long i = beg; i <= end; i++) {
synchronized (obj) {//用一个静态参量表示是只有一把锁
sum += i;
}
}
}
}
wait()
notify()
wait就是因为一写条件当前代码暂时不能执行(不满足条件,调用wait)此时该线程进入休息状态,不能参与cpu时间片的争夺,其他线程可以争夺。
当该对象调用notify()或者有其他线程对象调用notifyAll()才可以重现参与抢夺尝试执行代码。
public void takeChopsticks()
{
//拿筷子时只要有一个筷子已经被占用,就要放弃拿筷子,并释放锁进入等待状态wait
//notify得到通知后,再进入就绪状态,重新竞争锁
//调用某个对象的wait()方法能让当前线程阻塞,并且当前线程必须拥有此对象的monitor(即锁,或者叫管程)
if (noChopsticks[pos]||noChopsticks[((pos - 1) + 5) % 5]) {
synchronized (obj) {
try {
obj.wait();
//我只有一根筷子,没法吃饭,等着,该条线程先别动,释放锁让其他线程竞争
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//
noChopsticks[pos]=true;
noChopsticks[((pos - 1) + 5)%5]=true;
}
}
//放下筷子时,放锁并且通知所有等待的线程可以尝试抢夺cpu的时间片了
//notify()和notifyAll()的作用,则是唤醒当前对象上的等待线程;notify()是唤醒单个线程,而notifyAll()是唤醒所有的线程。
public void putDownChopsticks(int i) {
synchronized(obj) {
noChopsticks[pos]=false;
noChopsticks[((pos - 1) + 5)%5]=false;
System.out.println("Activity"+i+": The philosopher-" + Thread.currentThread().getName() + " has finished eaten");
obj.notifyAll();
//释放锁后其他线程就可以继续竞争了,其他线程会再次进行if判断
}
}
}