Object
Object是所有类的超类,只有Object没有父类;
注:如果某个数据没有指定明确的类型或者暂时不确定的话,都可以把它置为Object数据类型,可以理解为该类型兼容一切。无论什么样的数据类型都可以使用Object来接受,可以把它当做暂时的容器,等确定了数据类型后再把Object的数据转换成对应的数据类型即可。
封装类赋值注意事项
直接使用Float、Long、Double类赋值的时候,需要在数值后面加上对应的类型标识,即F、L、D。
Java语言三大特性
封装、继承、多态
封装:隐藏对象的属性和实现细节,仅对外提供公共访问方式。
优点:
- 提高代码的安全性
- 提高代码的复用性
- 高内聚:封装细节,便于修改内部代码,提高可维护性
- 低耦合:简化外部调用,便于调用者使用,便于拓展和协作
继承:子类可以继承父类的属性和功能,即继承了父类的数据和数据上的操作,又可以增加子类独有的数据和数据上的操作。
优点:代码复用
注:
- 子类拥有父类不是private的属性和方法
- 子类可以对父类进行拓展
多态:主要体现在Java的重写和重载上面。
构造器
构造方法只能被调用,不能被继承
- 如果父类有默认的构造方法(不带参数的方法),子类会自动调用父类该默认构造方法。
- 如果父类没有默认构造方法,就要显示的使用super()来调用父类构造方法,且要写在子类构造方法的第一行。
- 类中必定有构造方法,若不写,系统自动添加无参构造方法。接口不允许被实例化,所以接口中没有构造方法。
- 不能被static、final、synchronized、abstract和native修饰。
抽象类和接口
抽象类:
- 不能被实例化,需要子类继承它来实例化它的抽象方法。
- 抽象方法必须由子类来进行重写。
- 只要包含一个抽象方法的类,该类就必须要定义成抽象类,不管是否包含其他方法。
- abstract 不能与private、static、final或native并列修饰同一个方法。
- abstract不能与final并列修饰同一个类。为什么:final修饰的类不能被重写和继承,而abstract类必须被子类继承。
接口:
- 接口只能进行方法的声明,不允许提供方法的实现。
- 接口中的方法默认是public和abstract的,在接口中可以省略,但是在实现它的类中必须要用public修饰。
- 接口中的变量是public static final的。
- 如果一个接口不加public修饰,就称为友好接口,可被同一个包中的类使用。
- 如果父类实现了某个接口,那么子类也自然实现该接口。
- 接口可以被继承。
数组
声明数组方法:
-
int[] arr;
arr=new int[size];
-
int[] arr=new int[size];
-
int[] arr={1,2,3,4,5};
集合
Collection父接口:
代表一组任意类型的对象,无序、无下标、不能重复。
注:一些Collection允许相同的元素而另一些不行。一些能排序而另一些不行。
public class Demo {
public static void main(String[] args) {
Collection collection = new ArrayList();
collection.add("宝马");
collection.add("奔驰");
collection.add("奥迪");
collection.add("法拉利");
System.out.println("长度:"+collection.size());
System.out.println("-------第1种遍历Collection方式:增强for循环-------");
for (Object object : collection) {
String s = (String)object;
System.out.println(o);
}
System.out.println("-------第2种遍历Collection方式:迭代器-------");
Iterator it = collection.iterator();
while (it.hasNext()){
String str = (String)it.next();
System.out.println(str);
}
}
}
List子接口:有序、有下标、元素可以重复。
- ArrayList:使用数组方式实现,其容量随着元素的增加可以自动扩张,特点是查询效率高,而增加、删除效率低,线程不安全。
- Vector:Vector和ArrayList的存储特性是一样的,它是线程安全的,查询效率比ArrayList低。
- LinkedList:基于双向链表来实现的,链表中的每个节点都包含了对前一个和后一个元素的引用。元素的增加和删除操作效率高,查询效率不如ArrayList,也是线程不安全的。
为什么ArrayList查找快、增删慢,LinkedList查找慢,增删快?
ArrayList中的数据在内存中是连续的,成块的,查找的时候直接顺序遍历就可以了。而插入删除的时候,就要把修改的那个节点之后的所有数据都向后或向前移动,所以慢。
LinkedList是一个相互引用的节点组成的双向链表,当把数据插入到该链表某个位置时,该数据就会被组装成一个新的节点,只需改变链表中对应两个节点之间的引用关系,使它们指向新节点,即可完成插入,删除数据时,只需要删除对应节点的引用即可。
泛型
本质是参数化类型,把类型作为参数传递。
泛型类:class A
注:
- 泛型只能使用引用类型
- 不同泛型类型对象之间不能相互赋值
- 使用泛型类声明对象时,必须指定类中使用的泛型的具体实际类型
好处:
- 提高代码的重用性
- 防止类型转换异常,提高代码的安全性
泛型接口:interface A
泛型方法: 返回值类型
Set子接口
不允许重复元素,也不区分先后顺序,但允许元素是null值
- HashSet
基于Hash算法实现,其性能比TreeSet好,特点是增加删除元素快
HashSet每次添加新对象时,会使用equals方法,根据散列码来判断是否重复
- TreeSet
可以把元素自然排序
Map接口
不是继承Collection接口的,Map接口及其实现类主要是存储键值对。
- HashMap
存储方法为键值对,特点是线程不安全
- TreeMap
支持自然排序
- Hashtable
线程安全
遍历map集合方法:
- keySet()方法
- entrySet()方法
多线程
线程的生命周期:新建、就绪、运行、中断、死亡。
创建线程的方法:
- 继承Thread方式创建线程
//1.继承Thread类
public class TestThread01 extends Thread {
//2.重写run()方法
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("我是傻逼------"+i);
}
}
//main()方法,主线程
public static void main(String[] args) {
//实例化一个对象
TestThread01 testThread01 = new TestThread01();
//3。调用start()方法
testThread01.start();
for (int i = 0; i < 200; i++) {
System.out.println("我是大聪明------"+i);
}
}
}
- 实现Runnable接口方式创建线程**(推荐)**
//1.实现Runnable接口
public class TestThread03 implements Runnable {
//2.重写run()方法
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("我是牛马-------"+i);
}
}
public static void main(String[] args) {
//3.实例化一个线程对象
TestThread03 testThread03 = new TestThread03();
//4.通过Thread对象,代理TestThread03开启线程
new Thread(testThread03).start();
for (int i = 0; i < 200; i++) {
System.out.println("我是帅比-------"+i);
}
}
}
注:以上两种方式创建线程,都需要重写run()方法。
- 实现Callable接口(了解)
需要重写的是call()方法
终止线程的方法:(通过标识位,停止线程)
步骤:
- 线程类中定义一个标识
- run方法中使用该标识
- 提供对外的方法改变该标识
- 调用该改变标识的方法
public class ThreadStop implements Runnable {
//1.定义一个标识
private boolean flag = true;
//2.线程体使用该标识
@Override
public void run() {
int i = 0;
while (flag) {
System.out.println("Thread is running..."+i++);
}
}
//3.提供改变标识的方法
public void stop(){
this.flag = false;
}
public static void main(String[] args) {
ThreadStop threadStop = new ThreadStop();
new Thread(threadStop).start();
for (int i = 0; i <= 1000; i++) {
System.out.println("main---"+i);
if (i==900){
//4.调用改变标识的方法
threadStop.stop();
System.out.println("线程该停止了");
}
}
}
}
线程休眠
sleep()方法实现倒计时
public class ThreadSleep {
public static void tenDown() throws InterruptedException {
int num = 10;
while(true){
Thread.sleep(1000);
System.out.println(num--);
if (num<=0){
break;
}
}
}
public static void main(String[] args) {
try {
tenDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
线程礼让
不一定都礼让成功,看cpu心情
public class ThreadYield implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"开始执行");
Thread.yield();
System.out.println(Thread.currentThread().getName()+"停止执行");
}
public static void main(String[] args) {
ThreadYield threadYield = new ThreadYield();
new Thread(threadYield,"A").start();
new Thread(threadYield,"B").start();
}
}
线程优先级
范围:1-10
默认:5
获取优先级:getPriority()
设置优先级:setPriority(),在start()前设定
注意:优先级低只是意味着获得调度的概率低,并不是优先级低就不会被调用了,最终是看CPU的调度
守护(daemon)线程
- 线程分为用户线程和守护线程
- 虚拟机必须确保用户线程执行完毕
- 虚拟机不用等待守护线程执行完毕
- 守护线程:后台记录操作日志、监控内存、垃圾回收等线程
setDaemon()方法设线程为守护线程
public class TestDaemon {
public static void main(String[] args) {
God god = new God();
You you = new You();
Thread godThread = new Thread(god);
Thread youThread = new Thread(you);
godThread.setDaemon(true);
godThread.start();
youThread.start();
}
}
class God implements Runnable{
@Override
public void run() {
while(true){
System.out.println("上帝一直保佑着你");
}
}
}
class You implements Runnable{
@Override
public void run() {
for (int i = 0; i < 36500; i++) {
System.out.println("你一直快快乐乐地活着");
}
System.out.println("=======Goodbye World=======");
}
}
死锁
多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能运行,而导致两个或多个线程都在等待对方释放资源,都停止执行的情形。某一个同步块同时拥有“两个以上对象的锁”是,就可能会发生“死锁”的问题。
产生死锁的四个必要条件:
- 互斥条件:一个资源每次只能被一个进程使用。
- 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
- 不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺。
- 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
线程同步
方法1:添加synchronized关键字
synchronized方法和synchronized块。
注:对会改变的变量加锁,即需要增删改地对象
(只要涉及到并发,最好给方法添加synchronized关键字)
方法2:使用ReentrantLock实现同步
线程协作
解决生产者/消费者模式
- 管程法
- 信号灯法
线程池
经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大
思路:
提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁,实现重复利用。
好处:
- 提高响应速度(减少了创建新线程的时间)
- 降低资源消耗(重复利用线程池中线程,不需要每次都创建)
- 便于线程管理,如corePoolSize:核心池的大小;maximunPoolSize:最大线程数;keepAliveTime:线程没有任务时最多保持多长时间后会终止
public class MyThreadPool {
public static void main(String[] args) {
//1.创建服务,创建线程池
ExecutorService service = Executors.newFixedThreadPool(10);
//2.执行
service.execute(new MyPool());
service.execute(new MyPool());
service.execute(new MyPool());
service.execute(new MyPool());
//3.关闭连接
service.shutdown();
}
}
class MyPool implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
产生死锁的四个必要条件
- 互斥条件:该资源任意一个时刻只由一个线程占用
- 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放
- 不剥夺条件:线程已获得的资源在未使用完之前不能被其他线程强行剥夺,只有自己使用完毕后才释放资源
- 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系
Lambda表达式
Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。
使用 Lambda 表达式可以使代码变的更加简洁紧凑。