JAVA基础
-
- 第4/5/6章 面向对象
-
- Day 8 (P198-P218)
-
- 1、形参与实参
- 二、值传递机制
- 3.属性(成员变量)vs 局部变量
- Day 9 (P219-P241)
-
- 1.包装和隐藏
- 2.权限修饰符
- 3、构造器
- 4、UML类图
- 5、this关键字
- 6、package使用关键词
- 7、MVC设计模式
- 8、import关键字
- Day 10 (P242-P264)
-
- 1、项目2
- 2、继承性
- Day 11 (P265-P284)
-
- 1、方法重写(overwrite)
- 2、super关键字
- 3、多态性
- Day 12 (P285-P311)
-
- 1、向下转型
- 2、instanceof关键字
- 3、Object类
- 4、==
- 5、equals()
- 6、toString()
- 7、单元测试
- 8.使用包装
- Day 13 (P312-P338)
-
- 1、static关键字
- 2.内存结构图
- 3.单例设计模式
- 4、理解main方法
- 5、代码块
- 6、final关键字
- Day 14(P339-P366)
-
- 1、abstract
-
- abstract介绍
- 抽象的应用
- 匿名子类对象抽象匿名子类对象
- 2、接口
-
- 接口介绍
- 接口应用
- 接口笔试题
- java8中接口的新特性
- 抽象与界面的异同
- 3、内部类
- 4.关键词总结
- 第7章 异常处理
-
- Day 15(P367-P385)
-
- 1.异常概述和异常系统结构
- 2、常见异常
- 3、try-catch-finally
-
- 异常处理:抓抛模型
- try-catch-finally
- 4、throws
-
- 重写方法的规则
- 开发中异常处理的选择
- 5.手动抛出异常:throw
- 6.用户定制异常类别
- 第8章 多线程
-
- Day 15 (P406-P427)
-
- 1、程序、程序、线程
- 2.线程的创建和使用
-
- 方法一:继承Thread类
- 方法二:实现Runnable接口
- 对比两种方法
- Day 16(P428-P447)
-
- 3.线程的生命周期
- 4、线程同步
-
- 方法1:同步代码块
- 方法二:同步法
- 总结同步方法
- 线程安全单例模式:懒汉风格
- 死锁
- Lock(锁)
- 5.线程通信
-
- 经典例:生产者/消费者问题
- 6、JDK5.创建新线程的方法
-
- 实现Callable接口
- 使用线程池
- 7、总结
- 第9章 JAVA常用类
-
- 1.与字符串相关的类别
-
- String类别及常用方法
- StringBuffer/StringBuilder
- 2、JDK8前日期时间API
-
- System静态方法
- Date类
- Calendar类
- SimpleDateFormat类
- 3、JDK八中新日期时间API
-
- LocalDate/LocalTime/LocalDateTime
- Instant
- DateTimeFormatter
- 其他类
- 4、JAVA比较器
-
- Comparable接口
- Comparator接口
- 5、System类
- 6、Math类
- 7、BigInteger与BigDecimal
- 第10章 枚举类&注解
-
- 枚举类
- 1.如何定义枚举类
- 2.如何使用关键字enum定义枚举类
- 3、Enum类的主要方法
- 4.实现界面枚举类
- 注解
- 第11章 JAVA集合
-
- 1、JAV集合框架概述
- 2、Collection接口方法
- 3、Iterator迭代器接口
- 4、Collection子接口一:List
-
- ArrayList源码分析
- LinkedList源码分析
- 5、Collection子接口二:Set
-
- HashSet
- LinkedHashSet
- TreeSet
- set习题
- 6、Map接口
-
- Map实现类结构
- Map结构
- HashMap JDK7
-
- 底层实现
- 源码分析
- HashMap JDK8
-
- 底层实现
- 源码分析
- LinkedHashMap
- Map中定义的方法
- TreeMap
- Properties
- 7、Collections工具类
- 第12章 泛型
-
- 1、为什么要有泛型
- 2、在集合中使用泛型
- 3、自定义泛型结构
- 4、泛型在继承上的体现
- 5、通配符的使用
- **第13章IO流**
-
- 1、File类的使用
- 2、IO流原理及流的分类
- 3、节点流(或文件流)
-
- 读入
- 写出
- 4、缓冲流
- 5、转换流
- 6、标准输入、输出流
- 7、打印流
- 8、数据流
- 9、对象流
-
- 序列化和反序列化
- Serializable接口
- 10、随机存取文件流
- 11、NIO.2中Path、Paths、Files类的使用
- 第14章 网络编程
-
-
- 1、网络通信要素概述
- 2、通信要素1:IP和端口号
- 3、通信要素2:网络协议
- 4、TCP网络编程
- 5、UDP网络编程
- 6、URL编程
-
- 第15章 JAVA反射机制
-
- 1、Java反射机制概述
- 2、理解Class类并获取Class实例
- 3、类的加载与ClassLoader的理解
- 4、创建运行时类的对象
- 5、获取运行时类的完整结构
- 6、调用运行时类的指定结构
- 7、反射的应用:动态代理
- 第16章JAVA8 的其他新特性
-
- 之前提到的JAVA8新特性:
-
- java8中接口新特性
- JDK8中新日期时间API
- JDK8中注解的新特性
- HashMap JDK8
- 其他新特性
-
- 1、Lambda表达式
- 2、函数式(Functional)接口
- 3、方法引用与构造器引用
- 4、强大的Stream API
- 5、Optional类
- 第17章 JAVA9 & JAVA10 & JAVA11新特性
第1章 JAVA语言概述
第2章 基本语法
第3章 数组
第4/5/6章 面向对象
- 类和类的成员:属性、方法、构造器、代码块和内部类
- 封装、继承和多态
- 关键字及其使用
Day 8 (P198-P218)
1、形参与实参
形参:方法定义时,声明的小括号内的参数
实参:方法调用时,实际传递给形参的数据
2、值传递机制
方法的形参传递机制:值传递
如果参数是基本数据类型,此时实参赋给形参的是实参真实存储的数据值
如果参数是引用数据类型,此时实参赋给形参的是实参存储数据的地址值
3、属性(成员变量)vs 局部变量
相同点:
1、定义变量的格式:数据类型 变量名=变量值
2、先声明后使用
3、变量都有其作用域
不同点:
1、在类中声明的位置不同,属性直接定义在类的{}内,局部变量声明在方法内,方法形参,代码块内,构造器 形参、构造器内部的变量
2、权限修饰符:
属性:可以在声明属性时,指明其权限,使用权限修饰符
常用的权限修饰符:private、public、缺省、protected
局部变量:不可以使用权限修饰符
3、默认初始化值
属性:类的属性,根据其类型都有默认初始化值
整型(byte、short、int、long):0
浮点型(float、double):0.0
字符型(char):0(或’’)
布尔型(boolean):false
引用数据类型(、类、数组、接口):null
局部变量:没有默认初始化,在调用局部变量之前一定要显式化赋值
特别的,形参在调用时赋值即可
4、在内存中加载的位置
属性:加载到堆空间中
局部变量:加载到栈空间
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r1RExbh9-1639482821176)(C:Users86187DesktopMK笔记JAVA基础变量.png)]
Day 9 (P219-P241)
1、封装与隐藏
高内聚:类的内部数据操作细节自己完成,不允许外部干涉
低耦合:仅对外暴露少量的方法用于使用
2、权限修饰符
1、四种权限(从小到大):private,缺省(default),protected,public
修饰符
类内部
同一个包
不同包的子类
同一个工程
private
Yes
缺省
Yes
Yes
protected
Yes
Yes
Yes
public
Yes
Yes
Yes
Yes
2、4种权限可以用来修饰类及内部类的内部结构:属性、方法、构造器、内部类
修饰类只能用缺省、public
总结封装性:JAVA提供了4种权限修饰符来修饰类及类的内部结构,体现类及类的内部结构在被调用时的可见性的大小
3、构造器
1、构造器的作用:创建对象、初始化对象的属性
如果没有显式的定义类的构造器,则系统默认提供一个空参的构造器
一个类中定义的多个构造器彼此构成重载
2、属性赋值的选后顺序:①默认初始化 ②显式初始化 ③构造器中赋值 ④通过对象.方法或对象.属性赋值
3、只要造对象,就一定会使用构造器,这句话任何时候都是对的
4、UML类图
5、this关键字
1、方法的形参和类的属性同名时,必须显式使用this.变量的方式,表明此变量是属性,而非形参
2、this可以调用属性、方法和构造器
3、可以显式使用"this(形参列表)"方式,调用本类中指定的其他构造器
"this(形参列表)"必须声明在当前构造器的首行
构造器中不能使用 "this(形参列表)"的方式调用自己
构造器内部最多只能声明一个"this(形参列表)"调用其他构造器
6、package关键字的使用
1、使用package声明类或者接口所属的包,声明在源文件的首行
2、每“.”一次,就代表一层文件目录
3、同一个包下,不能命名同名的接口和类,不同包下可以声明同名的接口和类
7、MVC设计模式
将程序分成三个层次:视图模型层(view)、控制器层(controller)与数据模型层(model)[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bcVUFlqt-1639482821181)(C:Users86187AppDataRoamingTypora ypora-user-imagesimage-20211112170536925.png)]
8、import关键字
1、在源文件中使用import导入指定包下的类和接口
2、如果使用的类或者接口时java.lang包下定义的,则可以省略import结构
如果使用的类或者接口是本包下定义的,则可以省略import结构
3、如果在源文件中使用了不同包下的同名类,则必须至少有一个类需要以全类名的方式显示
4、使用“xxx.*”的方式可以调用包中的所有结构,但如果是子包,仍需要显式调用(我的理解就是没有子包,都是并列的包)
5、import static:导入类或接口中的静态结构(意思就是导入属性或者方法),单import是导入类或接口
Day 10 (P242-P264)
1、项目2
1、动态数组,大小随时改变的写法
2、删除数组中的元素,后面的元素要覆盖前面的,不能直接指空
2、继承性
1、继承性的优点:
①减少代码冗余,提高代码复用性
②便于功能的扩展
2、继承的格式:classA extends classB{}
A:子类、派生类、subclass
B:父类、基类、超类、superclass
体现:一旦子类继承父类之后,子类就获取了父类中声明的属性和方法。
特别的,父类中声明为private的属性或者方法,仍然认为获取了父类中私有的结构,只是因为封装性的影响, 子类不能直接调用。子类继承父类之后,可以声明自己持有的属性或方法实现功能的扩展。
3、JAVA中关于继承的规定
①支持单继承和多层继承,不允许多重继承
一个子类只能有一个父类,一个父类可以派生多个子类
②直接父类和间接父类
③所有类都直接或间接继承于Object类
Day 11 (P265-P284)
1、方法重写(overwrite)
1、重写:子类继承父类以后,可以对父类中同名参数的方法进行覆盖操作
2、重写之后,当创建子类对象以后,通过子类对象调用子父类中的同名同参数的方法时,实际执行的是子类重写父类的方法
3、重写的规定:
方法的声明:权限修饰符 返回值类型 方法名(形参列表)throws 异常的类型{ 方法体 }
子类中叫的方法,父类中叫的方法
①子类重写的方法的方法名和形参列表与父类被重写的方法名和形参列表相同
②子类重写的方法的权限修饰符不小于父类被重写方法的权限修饰符
特殊情况:子类不能重写父类中声明为private权限的方法
③返回值类型:
父类被重写的方法的返回值是void:子类重写的方法只能是void
父类被重写的方法的返回值是引用类型:子类重写的方法只能和父类相同或者为其子类
父类被重写的方法的返回值是基本类型:子类重写的方法只能是相同的基本类型
④子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型
⑤必须是非static
2、super关键字
1、super可以用来调用属性、方法和构造器。
2、当子类和父类中定义了同名的属性时,要想在子类中调用父类中声明的属性则必须显式使用super.属性。
当子类重写了父类中的方法之后,要在子类中调用父类中被重写的方法则必须显式使用super.属性。
3、super调用构造器
可以在子类中使用 super(形参列表)的方式调用父类中声明的指定的构造器,必须声明在首行
在类的构造器中 this( 形参列表 )和super(形参列表)只能二选一,在构造器没有显式声明时,默认调用父类中的空参构造器
3、多态性
1、父类的引用指向子类的对象——对象的多态性
2、多态的使用:虚拟方法的调用
有了对象的多态性之后,在编译期只能调用父类中声明的方法,编译期实际执行的是子类重写父类的方法
3、多态性使用前提:①有类的继承关系 ②要有方法的重写
4、对象的多态性只适用于方法不适用于属性(还是父类的属性)
5、多态性是一个运行时行为,要到运行的时候才知道具体是哪一个,而重载是编译时行为,编译的时候就知道具体是哪一个
Day 12 (P285-P311)
1、向下转型
1、有了对象的多态性之后,内存中实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型,导致编译时只能使用父类中声明的属性和方法。使用强制类型转换符之后就可以使用子类的属性和方法,即向下转型。
2、自动类型提升和强制类型转化(有可能会转不成功
3、P289:向下转型的几个问题
(那几个练习还蛮重要的感觉)
2、instanceof关键字
1、a instanceof A:判断对象a是否为A的实例,如果是返回true。
2、使用情景:避免在向下转型时出现异常,先进行判断。
3、Object类
1、Object类是所有类的根父类,类中的属性和方法具有通用性,Object类中只声明了一个空参构造器
2、Object类中的方法:
- clone(),克隆一个对象
- funalize(),对象调用的用于垃圾回收的方法(不要主动调用,由垃圾回收器调用)
- / / getClass() / hashCode() / wait() / notify() / notifyAll()
- 数组也作为Object类的子类出现,可以调用Object类中声明的方法
4、==
1、可以使用在基本数据类型变量和引用数据类型变量中
2、基本数据类型变量:比较保存的数据(类型可以不同)
引用数据类型变量:比较地址值,即两个引用是否指向同一个对象实体
3、基本数据类型除了boolean以外都会有自动类型提升所以不用太关注类型,关注值。引用类型的话类型都不同地址必不可能相同
5、equals()
1、基本数据类型不是类,无法调用equals()方法,只适用于引用数据类型
2、Object类中equals()方法的定义:
public boolean equals(Object obj){
return (this==obj);
}
说明Object类中equals()方法和==相同,比较地址值,即两个引用是否指向同一个对象实体
3、StringDateFile包装类都重写了Object类中的equals()方法,重写后比较的是属性是否相同(内容是否相同)
4、自定义的类如果使用equals()方法,也通常是比较实体内容,那就需要重写。重写的步骤:先判断一下,如果直接返回true,然后判断是否为同一类型,是的话向下转型然后比较需要的属性
系统可以自动生成equals()方法比较你需要的属性。
5、x.equals(null)永远都是false,比较的前提是类型相同
6、常量池
String s1="BB";
String s2="BB";
System.out.println(s1==s2);//true
两个对象都在常量池中,两个对象的地址一样
6、toString()
1、当我们输出一个对象的引用时,实际上是调用当前对象的toString()
2、Object类中toString()的定义
public String toString(){
return getClass().getName()+"@"+Integer.toHexString(hashCode());
}
输出:类名@地址值(JVM中的虚拟地址值)
3、String/Date/File/包装类把toString()重写了,输出的是内容。自定义类也可以重写。
7、单元测试
(讲的是Eclipse的单元测试,要用的话就去找IDEA的)
8、包装类的使用
基本数据类型
包装类
byte
Byte
short
Short
int
Integer
long
Long
float
Float
double
Double
boolean
Boolean
char
Character
1、基本类型与包装类的转换
- 基本数据类型–>包装类:调用包装类的构造器
- 包装类–>基本数据类型:调用xxxValue()
2、JDK 5.0新特性:自动装箱和自动拆箱
int num=10;
Integer num1=num;//自动装箱:基本数据类型--->包装类
int num2=num1;//自动拆箱:包装类--->基本数据类型
3、基本数据类型、包装类—>String
//方式1:连接运算(其他基本类型都可以用这种方式)
int num=10;
String str=num+"";
//方式2:valueOf()
float f1=12.3f;
String str1=String.valueOf(f1);
4、String—>基本数据类型、包装类:调用包装类的parseXxx()
String str="123";
int num=Integer.parseInt(str);
可能会报NumberFormatException,比如"true"要转int就是不可能的,就会报错[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kk9L7pDt-1639482821183)(C:Users86187AppDataRoamingTypora ypora-user-imagesimage-20211119210908171.png)]
熟练掌握:
5、课后题
//题目1
Object o1=true?new Integer(1):new Double(2.1);
System.out.println(o1);//1.0
//三目运算符要求类型相同,int自动类型提升为double
Object o2;
if(true)o2=new Integer(1);
else o2=new Double(2.0);
System.out.println(o2);//1
//题目2
public void method(){
Integer i=new Integer(1);
Integer j=new Integer(1);
System.out.println(i == j);//false
Integer m=1;
Integer n=1;
System.out.println(m == n);//true
Integer x=128;
Integer y=128;
System.out.println(x == y);//false
}
①两个不同的对象
②Integer内部定义了IngeterCache()方法,其中定义了Ingeter[],保存了-128127的整数,如果给Ingeter赋值的范围在-128127以内,可以直接使用数组中的元素,不用去new了,如此可以提高效率
③超过了缓存池的大小,所以是new了两个不同的对象
Day 13 (P312-P338)
1、static关键字
1、static可以用来修饰:属性、方法、代码块、内部类
2、static修饰属性:静态变量(类变量),非静态变量(实例变量)
-
按是否有static修饰,属性又可分为静态属性和非静态属性
-
实例变量:创建了多个对象之后,每个对象独立拥有一套非静态属性,修改其中一个对象的非静态属性不会导致其他对象中同样的属性的修改
-
静态变量:多个对象共享同一个静态变量,当通过某一个对象修改静态变量时,其他对象的该变量也一起改变
-
静态变量随着类的加载而加载,早于对象的创建,可以通过“类.静态变量”的方式调用
由于类只会加载一次,静态变量在内存中也只有一份
-
静态属性举例:System.out,Math.PI
3、static修饰方法:静态方法
① 静态方法随着类的加载而加载,早于对象的创建,可以通过“类.静态方法”的方式调用
② 静态方法中,只能调用静态的方法和属性
非静态方法中,既可以调用静态的属性和方法,也可以调用非静态的
4、在静态方法内,不能室友this、super关键字
5、在开发中,如何确定一个属性要声明为static?
属性可以被多个对象共享,不会因为对象的不同而不同
6、在开发中,如何确定一个方法要声明为static?
工具类中的方法,习惯上声明为static,比如MathArraysCollections
操作静态属性的方法
7、使用举例
①MathArraysCollections等工具类
②单例模式
2、内存结构图
栈:局部变量
堆:new出来的结构,对象、数组
方法区:类的加载信息、静态域、常量池
class Chinese{
String name;
int age;
static String nation;
}
public static void main(String[] args){
Chinese.nation="中国";
Chinese c1=new Chinese();
c1.name="姚明";
c1.age=40;
Chinese c2=new Chinese();
c1.name="马龙";
c1.age=30;
System.out.println(c1.nation);//中国
c1.nation="CHN";
System.out.println(c2.nation);//CHN
c2.nation="CHINA";
System.out.println(c1.nation);//CHINA
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rP0Vw7Pi-1639482821184)(C:Users86187DesktopMK笔记JAVA基础图片1.png)]
3、单例设计模式
-
单例设计模式就是采取一定的方法保证整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。
-
单例模式的优点:只生成一个实例,减少系统性能开销。使用例:getRuntime()
-
区分饿汉式和懒汉式:
饿汉式——缺点:先创建对象,对象加载时间过长;优点:线程安全
懒汉式——优点:延迟对象的创建;缺点:下述写法线程不安全
-
public class SingletonTest{ public static void main(String[] args){ Bank bank1=Bank.getInstance(); Bank bank2=Bank.getInstance(); System.out.println(bank1==bank2);//true } } //--------饿汉式单例模式 class Bank{ //1、私有化类的构造器 private Bank(){ } //2、内部创建类的对象 private static Bank instance=new Bank(); //3、提供公共的静态方法返回类的对象,可以在main方法中通过类名调用方法 public static Bank getInstance(){ return instance; } } //--------懒汉式单例模式 class Order{ //1、私有化类的构造器 private Order(){ } //2、声明当前类的对象 private static Order instance=null; //3、声明public、static的返回当前类的对象 public static Order getInstance(){ if(instance==null){ instance=new Order(); } return instance; } }
4、理解main方法
- main()方法是程序的入口
- main()方法也是一个普通的静态方法
- main()方法可以作为我们与控制台交互的方式
5、代码块
class Person{
//属性
String name;
int age;
static String desc="我是一个人";
//构造器
public Person(){
}
public Person(String name,int age){
this.name=name;
this.age=age;
}
//非静态代码块
{
System.out.println("block")
}
//静态代码块
static{
System.out.println("static block")
}
//方法
public void eat(){
System.out.println("吃饭")
}
@Override
public String toString(){
return "Person[name="+name+",age="+age+"]";
}
}
-
代码块用于初始化类和对象,如果有修饰的话只能用static,加上static为静态代码块,不加为非静态
-
静态代码块:
①内部可以有输出语句
②随着类的加载而执行,而且只执行一次
③如果一个类中使用了多个静态代码块,则按照声明的先后顺序执行
④静态代码块的执行优先于非静态代码块的执行
⑤只能调用静态的属性或方法
-
非静态代码块
①内部可以有输出语句
②随着对象的创建而执行,每创建一个对象就执行一次,可以在创建对象时对对象的属性进行初始化
③如果一个类中使用了多个非静态代码块,则按照声明的先后顺序执行
④可以调用静态和非静态的属性或方法
-
给属性赋值的顺序
①默认初始化
②显式初始化/在代码块中赋值(看谁写在前面)
③构造器中初始化
④创建对象,通过对象.属性或者对象.方法赋值
-
代码块的练习值得看一看:P332
6、final关键字
-
final修饰类:此类不能被其他类所继承,比如:String、System、StringBuffer
-
final修饰方法:此方法不能再被重写,比如:Object类中的getClass()
-
static final用来修饰属性:全局常量
-
final修饰变量:此时的变量就称为是一个常量
final修饰属性:可以考虑赋值的位置:显式初始化,代码块中初始化、构造器中初始化
final修饰局部变量:尤其是用final修饰形参时,表明此形参是一个常量,当调用此方法时,给常量形参赋实参,一旦赋值就只能在方法体内使用此形参,但不能重新赋值
Day 14(P339-P366)
P340后面改单例模式的权限修饰符,private改public有必要听一下
1、abstract
abstract介绍
-
可以用于修饰:类(抽象类)、方法(抽象方法)
-
abstract修饰类:
①此类不能实例化(也就是不能创建这个类的对象
②虽然自己不能实例化,但是子类会调用父类的构造器,所以抽象类中一定有构造器
-
abstract修饰方法
① 抽象方法只有方法的声明没有方法体,所在的类一定是抽象类。因为如果类不是抽象的,那这个类就可以造对象,可以造对象就可以调用。反之抽象类中可以没有抽象方法。
② 若子类重写了子类重写了父类所有的抽象方法才能实例化,如果没有全部重写,那么子类也是抽象类,也需要用abstract修饰
③ abstract不能用来修饰私有方法、静态方法、final关键字修饰的方法、final关键字修饰的类
因为如果两个方法都是static,不认为两个方法是重写或者覆盖,所以abstract用来修饰静态方法,就无法重写
抽象的应用
模板方法设计模式。在软件开发中实现一个算法时,整体步骤很固定、通用,这些步骤在父类中写好,某些易变的和不确定的部分可以抽象出来给子类实现。
抽象类的匿名子类对象
public static void main(String[] args){
//匿名对象
eat(new son());
//非匿名类的非匿名对象
son John=new son();
eat(John);
//匿名子类对象
father f=new father(){
@Override
public void work() {
}
@Override
public void info(father i) {
}
};
}
//普通方法
public static void eat(father f){
System.out.println("吃饭");
}
//父类
public abstract static class father{
String name;
int age;
public abstract void work();//抽象方法不能有方法体
public abstract void info(father i);
}
//子类
public class son extends father{//继承
String name;
int age;
@Override
public void work(){
System.out.println("上学");
}
@Override
public void info(father i) {
System.out.println("name:"+i.name+" age:"+i.age);
}
}
2、接口
接口介绍
-
接口使用Interface来定义,和类是并列关系
-
接口的定义以及接口中的成员:
JDK7及以前:只能定义全局常量和抽象方法
- 全局常量:都是用public static final定义的,可以省略不写
- 抽象方法:public abstract
JDK8:除了全局常量和抽象方法,还可以定义静态方法和默认方法
-
接口中不能定义构造器,意味着接口不可以实例化,通过类去实现(implements)接口。
如果类覆盖了接口中所有的抽象方法,则可以创造实例;
如果类没有覆盖接口中所有的抽象方法,则该类仍为抽象类。
-
Java类可以实现多个接口——弥补了单继承性的缺陷
class AA extends BB implements CC,DD,EE
-
接口和接口之间可以继承,且可以多继承。接口的使用体现了多态性。
-
接口是一种规范,面向接口编程。
-
面试题:抽象类和接口有哪些异同?
接口应用
- 代理模式
代理设计就是为其他对象提供一张代理以控制对这个对象的访问
应用场景:安全代理、远程代理、延迟加载
分类:静态代理、动态代理
- 工厂模式
实现创建者和调用者的分离
接口笔试题
-
排错
interface A{ int x=0; } class B{ int x=1; } class C extends B implements A{ public void pX(){ System.out.println(x); } public static void main(String[] args){ new C().pX(); } }
问题:编译期不知道是要输出哪个x
System.out.println(super.x);//这个调用的是父类中的 System.out.println(A.x);//这个调用的是接口中的
-
排错
interface Playable{ void play(); } interface Bounceable{ void play(); } interface Rollable extends Playable,Bounceable{ Ball ball=new Ball("PingPong"); } class Ball implements Rollable{ private String name; public String getName(){ return name; } public Ball(String name){ this.name=name; } public void play(){ ball=new Ball("Football"); System.out.println(ball.getName()); } }
- Ball中的play()方法重写可以认为同时重写了Playable和Bounceable的play方法
- Ball ball是public final的,不能再修改,所以ball=new Ball(“Football”)是错误的
java8中接口新特性
JDK8:除了全局常量和抽象方法,还可以定义静态方法和默认方法(default关键字修饰)
- 接口中定义的静态方法只能通过接口来调用,接口.方法。
- 通过实现类的对象,可以调用接口中的默认方法,对象.方法。如果实现类重写了接口中的默认方法,调用时仍然调用的是重写以后的方法
- 如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的方法,子类在没有重写此方法的情况下调用的是父类中的方法——类优先原则
- 如果实现类实现了多个接口,而这个多个接口中定义了同名同参数的默认方法,在实现类没有重写方法的情况下会报”接口冲突“错误,此时需要重写。
- 如何在子类(或者实现类)调用父类、接口中被重写的方法,接口.super.方法
抽象类和接口的异同
相同点:不能实例化,都可以包含抽象方法
不同点:
- 把抽象类和接口(java7、java8)的定义、内部结构解释说明
- 类:单继承性,接口:多继承性,
3、内部类
-
Java允许将一个类A声明在另一个类B中,A为内部类,B为外部类
-
内部类的分类:成员内部类、局部内部类(方法内,代码块内,构造器内)
-
成员内部类
- 作为外部类的成员:可以调用外部类的结构,可以被static修饰
- 作为一个类:可以定义属性、方法、构造器,可以用final、abstract修饰,可以被继承
-
需要关注的问题:
-
如何实例化成员内部类的对象:外部类Person,静态内部类Brain,非静态内部类Lungs
静态成员内部类:new 外部类.内部类()
Person.Brain brain=new Person.Brain();
非静态成员内部类:先造对象,对象.new 内部类()
Person p=new Person(); p.Lungs lungs=p.new Lungs();
-
如何在成员内部类中区分调用外部类的结构
形参直接调,所在类的用this.结构,外部类的用外部类.this.结构
-
成员内部类和局部内部类在编译以后都会生成字节码文件
成员内部类:外部类$内部类名.class
局部内部类:外部类$数字 内部类名.class
-
在局部内部类的方法中,如果调用局部内部类所在的方法中的局部变量,该局部变量必须用final关键字修饰(JAVA8之后可以不写出来,但仍然还是final的)
public void Person(){ int num=10; class AA{//局部内部类 public void show(){//局部内部类的方法 num=20;//试图修改会报错 System.out.println(num);//调用局部内部类所在的方法中的局部变量 } } }
-
开发中局部内部类的使用
常用的局部内部类:
//方式一 public Comparable getCompareble(){ class MyComparable implements Comparable{//局部内部类 @Override public int compareTo(Object o){ return 0; } } return new MyComparable(); } //方式二 public Comparable getCompareble(){ return new Comparable(){ @Override public int CompareTo(Object o){ return 0; } }; }
-
4、关键字总结
static可以用来修饰:属性、方法、代码块、内部类
final关键字可以用来修饰:类、方法、变量(属性和局部变量)
abstract可以用于修饰:类、方法
第7章 异常处理
Day 15(P367-P385)
1、异常概述与异常体系结构
JAVA.lang.Throwable:
- Error:JAVA虚拟机无法解决的严重问题
- Exception:其他因变成错误或偶然的外在因素导致的一般性问题
- 编译时异常
- IOException
- ClassNotFoundException
- 运行时异常
- NullPointerException
- ArrayIndexOutOfBoundsException
面试题
-
常见的异常有哪些?举例说明
-
final/finally/finalize
-
throw/throws
throw:抛出一个异常类的对象,生成异常对象的过程,声明在方法体内
throws:属于异常处理的方式,声明在方法体处
-
String / StringBuffer / StringBuilder
-
ArrayList / LinkedList
-
HashMap / LinkedHashMap
-
重写/重载
-
==/equals
-
抽象类 / 接口
-
sleep() / wait()
2、常见异常
-
NullPointerException:空指针异常
public static void main(String[] args){ int[] arr=null; System.out.println(arr[0]);//空指针异常 }
-
ArrayIndexOutOfBoundsException:数组角标越界
public static void main(String[] args){ int[] arr=new int[3]; System.out.println(arr[3]);//数组角标越界 }
-
ClassCastException类型转换异常
public static void main(String[] args){ Object obj=new Date(); String s=(String)obj;//类型转换异常 }
-
NumberFormatException数值转换异常
public static void main(String[] args){ String s="abc"; int num=Intger.parseInt(s);//数值转换异常 }
-
InputMismatchException输入类型异常
public static void main(String[] args){ Scanner scanner=new Scanner(System.in); int score=scanner.nextInt(); System.out.println(score);//输入“abc”,输入类型错误 }
-
ArithmeticException算数异常
public static void main(String[] args){ int a=10; int b=0; System.out.println(a/b);//算数异常 }
3、try-catch-finally
异常的处理:抓抛模型
- “抛”:程序在正常执行的过程中,一旦出现异常,就会在异常代码出生成一个对应异常类的对象并将此对象抛出,其后的代码就不再执行。
- “抓”:可以理解为异常处理的方式:①try-catch-finally ②throws
try-catch-finally
try{
//可能出现异常的代码
}catch(异常类型1 变量名1){
//处理异常的方式1
}catch(异常类型2 变量名2){
//处理异常的方式2
}
......
finally{
//一定会执行的代码
}
public static void main(String[] args){
String s="abc";
try{
int num=Intger.parseInt(s);
System.out.println("-----1-----");
}catch(NumberFormatException e){
System.out.println("出现数值转换异常");//出现异常时采取的操作
}
System.out.println("-----2-----");
}
//1不会输出,2会输出,因为一旦出现异常下面的代码就不再执行
-
finally是可选的,可以不写。
-
使用try将可能出现异常的代码包装起来,在执行过程中,一旦出现异常,就会生成一个对应异常类的对象,根据此对象的类型去catch中进行匹配,一旦try中的对象匹配到某一个catch的时候就会进入异常的处理,一旦处理完成,如果没有写finally,就跳出当前的try-catch结构,继续执行其后的代码。
-
对异常的常用操作:①getMessage()返回String;②printStackTrace()
public static void main(String[] args){ String s="abc"; try{ int num=Intger.parseInt(s); }catch(NumberFormatException e){ System.out.println(e.getMessage());//输出For input string: "abc" e.printStackTrace();//非常具体的异常的位置和信息 } }
-
finally的使用:即使catch中也出现了异常,try和catch里面还有return语句,finally都会执行,就是不管怎样都一定会执行
public static void main(String[] args){ int a=10; int b=0; try{ System.out.println(a/b);//算数异常 }catch(ArithmeticException e){ e.printStackTrace(); }finally{ System.out.println("发生异常了"); } } //输出: java.lang.ArithmeticException: / by zero at ExceptionExer.ExceptionTest.main(ExceptionTest.java:8) 发生异常了
-
finally的使用场景:数据库连接、输入输出流、网络编程Socket等资源,JVM是不能自动回收的,我们需要自己手动的进行资源释放,此时的资源释放需要声明在finally中。
-
try-catch-finally结构可以嵌套
-
使用try-catch-finally处理编译时已成,使得程序在编译时不再报错,但运行时仍可能保持,相当于把编译时可能出现的异常延迟到运行时出现。一般不针对运行时异常使用try-catch-finally,针对编译时异常需要做处理。
4、throws
public static void main(String[] args){
try{//main方法对异常进行处理
method();
}catch(ArithmeticException e){
//处理
}
}
public static void method() throws ArithmeticException{//抛到main方法
int a=10;
int b=0;
System.out.println(a/b);//算数异常
}
throws+异常类型写在方法的声明处,指明此方法执行时,可能会抛出的异常类型,一旦方法体执行时出现异常仍然会异常代码处生成一个异常类的对象,此对象满足throws后异常类型时就会被抛出,异常代码后续的代码就(和try-catch-finally的不同点)。
方法重写的规则
子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型
开发中异常处理的选择
- 如果父类中被重写的方法没有throws方式处理异常,则子类重写的方法也不能用throws
- 执行的方法A中先后调用了其他方法,方法和方法之间又有递进关系,这几个方法用throws,A中用try-catch-finally
5、手动抛出异常:throw
异常的产生:①自动生成 ②手动抛出
6、用户自定义异常类
-
继承于现有的异常结构:RuntimeException、Exception
-
提供全局常量:serialVersionUID,序列版本号
-
提供重载的构造器
public class EcDef extends Exception{ static final long serialVersionUID=-33875164229948L; public EcDef(){ } public EcDef(String msg){ super(msg); } }
第8章 多线程
Day 15 (P406-P427)
正式的多线程从P415开始
1、程序、进程、线程
:一段静态的代码,静态对象
:正在运行的一个程序,是一个动态的过程,有生命周期。进程作为资源分配的单位,系统在运行时会为每个进程分配内存区域。
:进程可以进一步细化为线程,同一个进程可以支持多个线程就叫多线程
线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RzvwF1NH-1639482821185)(C:Users86187AppDataRoamingTypora ypora-user-imagesimage-20211126110036164.png)]
每个线程各自有一套虚拟机栈和程序计数器
每个进程各有一份方法区和堆
:一个JAVA应用程序至少有三个线程,main主线程,gc()垃圾回收线程,异常处理线程
:并行——多个CPU同时执行多个任务
并发——一个CPU同时执行多个任务
1、提高程序的响应。对图形化界面更有意义,可增强用户体验
2、提高计算机系统CPU的利用率
3、改善程序结构,进程分为线程独立运行,有利于理解和修改
2、线程的创建和使用
方式一:继承于Thread类
1、创建Thread类的子类
2、重写Thread类的方法
3、创建Thread类的子类对象
4、通过此对象调用start()
class MyThread extends Thread{
@Override
public void run(){
for (int i = 0; i < 10; i++) {
System.out.println(i);
}
}
}
public static void main(String[] args) {
MyThread t=new MyThread();
t.start();//①启动当前线程 ②调用当前线程的run()
//匿名子类
new Thread(){
@Override
public void run(){
}
}.start();
}
//执行的先后顺序并不会按照写的先后顺序,两个线程是并行的
tips:
- 不能通过run()的方式执行线程,用run()只是普通的调用方法
- 如果还要再启动一个线程,就再新造一个MyThread对象,再start()
Thread里的常用方法:
- start():①启动当前线程 ②调用当前线程的run()
- run():通常需要重写此方法
- currentThread():静态方法,返回执行当前代码的线程
- getName():获取当前线程的名字
- setName():设置当前线程的名字
- yield():释放当前CPU的执行权
- join():在线程A中调用B的该方法,线程A进入阻塞状态直到线程B执行完之后线程A才继续执行
- sleep():让当前线程强制阻塞
线程的优先级:
- MAX——10,MIN——1,NORM——5(默认的优先级)
- 涉及的方法:getPriority()——返回线程的优先值,setPriority()——改变线程的优先级
- 高优先级的线程不一定就先执行,只是大概率先执行
方式二:实现Runnable接口
-
创建一个实现Runnable接口的类
-
实现类实现Runnable中的抽象方法:run()方法
-
创建实现类的对象
-
将对象作为参数传递到Thread类的构造器中,创建Thread类的对象
-
通过此对象调用start()方法
class MyRun implements Runnable{//创建一个实现Runnable接口的类 @Override public void run(){//实现类实现Runnable中的抽象方法:run()方法 for (int i = 0; i < 10; i++) { System.out.println(i); } } } public static void main(String[] args) { MyRun r=new MyRun();//创建实现类的对象 Thread t=new Thread®;//将对象作为参数传递到Thread类的构造器中,创建Thread类的对象 t.start();//通过此对象调用start()方法
//再启动一个线程 Thread t1=new Thread(r); t1.start();
}
两种方式的对比
- Runnable没有单继承性的局限性,更适合多个线程共享数据的情况
- 相同点:都需要重写run并且将线程要执行的逻辑声明在run()中
Day 16(P428-P447)
3、线程的生命周期
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ISKMrQs3-1639482821187)(C:Users86187AppDataRoamingTypora ypora-user-imagesimage-20211201152708151.png)]
4、线程的同步
线程安全问题:
- 多个线程执行的不确定性引起执行结果的不稳定性
- 多个线程对数据的共享会造成操作的不完整性,会破坏数据
解决:当一个线程A在操作共享数据的时候其他线程不能参与,直到线程A操作完毕,也就是同步机制。
方式一:同步代码块
synchronized(同步监视器){
//需要被同步的代码
}
-
操作共享数据的代码即为需要被同步的代码
-
共享数据:多个线程共同操作的变量
-
同步监视器,俗称锁。任何类的对象都可以充当锁,多个线程必须共用同一把锁。
-
在实现Runnable接口创建多线程的方式中,我们可以考虑使用this充当同步监视器;而在继承Thread类创建多线程的方式中,可以考虑使用当前类
//同步代码块+实现Thread类 class MyThread extends Thread{ private static int ticket=100; private static Object obj=new Object(); @Override public void run(){ synchronized (MyThread.class){//类只会加载一次 //synchronized (this){//实现Runnable接口创建多线程的方式用this //synchronized(obj){//造了三个对象,有三把锁,所以不能保证线程安全 for (tikect>0) { System.out.println(getName()+“买票,票号为:”+ticket); ticket–; } } } } public static void main(String[] args) { MyThread t1=new MyThread(); MyThread t2=new MyThread(); MyThread t3=new MyThread(); t1.start(); t2.start(); t3.start(); }
方式二:同步方法
如果操作共享数据的代码完整的声明在一个方法中,不妨将此方法声明为同步的
//同步方法+实现Runnable
class MyRun implements Runnable{
private static int ticket=100;
@Override
public void run(){
show();
}
public synchronized void show(){//这里的同步监视器就是this
for (tikect>0) {
System.out.println(getName()+"买票,票号为:"+ticket);
ticket--;
}
}
}
public static void main(String[] args) {
MyRun r=new MyRun();
Thread t1=new Thread(r);
Thread t2=new Thread(r);
Thread t3=new Thread(r);
t1.start();
t2.start();
t3.start();
}
//同步方法+实现Thread类
class MyThread extends Thread{
private static int ticket=100;
@Override
public void run(){
show();
}
public static synchronized void show(){//必须是静态的,这里的同步监视器是当前类
for (tikect>0) {
System.out.println(Thread.currentThread.getName()+"买票,票号为:"+ticket);
ticket--;
}
}
}
同步方法总结
-
同步代码块
-
继承Thread类:用当前类当作同步监视器(MyThread.class)
-
实现Runnable接口:用this当同步监视器
-
-
同步方法
-
继承Thread类:直接用synchronized修饰方法,this当同步监视器
-
实现Runnable接口:需要用synchronized static修饰方法,当前类当同步监视器
-