一、杂项
基础
-
局部变量 只在方法里有用,要初始值
-
switch只能用int byte short char
-
变成包的样子在包里面,要在最开头写,package 路径;如(package com.sun.base;)
要在一个包的类里面用到别的包的类,需要导包,import 路径;如(import com.sun.operator.Hello;)
若,需要用到此包中所有类,就直接用号,如(import com.sun.operator.)
-
JavaDoc的注释方法可以生成一个说明文件,详细介绍这个Java文件
-
可变长参数:……要在最后。
-
单引号中的英文是int,双引号是String。
类
-
类变量 方法里面就不用new一个东西再使用,可以像局部变量一样直接用。
实例变量 就要new一个东西再用sout(东西.变量)来输出。
-
一个Java类中可以有多个class类,但是只能有一个public
-
Java语言是以类为程序的基本单位
-
命令行运行包里面的类,要回退到包的那个文件夹,然后用包路径com.xxx.xxx.xxx去运行。构建不用。
-
用static修饰符修饰的方法是属于整个类的类方法,调用时,使用类名作为前缀。不用static的是属于某个具体类对象或实例的方法,调用时,使用具体的对象名作为前缀。
static是最早出生的,如果要调用一个不是static的就拿不到,因为还没生出来。
-
内部类可以使用外部类的私有属性。
//定义一个函数式接口(只有一个方法的接口)
interface I_Like{
void Like();
}
//1、常见
class Like1 implements I_Like{
@Override
public void Like() {
System.out.println("I like1 you");
}
}
public class 多种类的对比 {
//2、静态内部类
static class Like2 implements I_Like{
@Override
public void Like() {
System.out.println("I like2 you");
}
}
public static void main(String[] args){
//定义接口对象
I_Like like;
//3、局部内部类
class Like3 implements I_Like{
@Override
public void Like() {
System.out.println("I like3 you");
}
}
like = new Like1();
like.Like();
like = new Like2();
like.Like();
like = new Like3();
like.Like();
//4、匿名内部类,没有类的名称,需要借助接口或父类
like = new I_Like(){
@Override
public void Like() {
System.out.println("I like4 you");
}
};
like.Like();
//5、lambda形式
like=()->System.out.println("I like5 you");
like.Like();
}
}
File类的一些方法
canRead() 文件是否可读
length() 文件长度
getAbsolutePath() 文件绝对路径
数组
动态初始化数组的格式:数组类型[] 数组名称 = new 数组类型 [数组长度];
静态初始化数组:数组类型[] 数组名称 = new 数组类型 []{};
左侧数据类型:表示数组中保存的数据是什么类型;
左侧中括号:代表是一个数组;
右侧数据类型:与左侧相同;
右侧中括号:表示数组中可以保存多少数据;
二维的在后面再加个中括号就行
a.length是第几行,a[].length是第几行有多少数。
二、方法
-
方法重载,定义一样的方法,一个int的和一个double的,根据输入的数据会自动使用相应类型的方法。
-
方法重写,在子父类间,将父类一样的方法重新去编写。
-
私有变量/方法:同一个包里面的不同类不能调用。
友好变量/方法:同一个包里面的不同类都可以调用。
public>protected>友好的>private
-
封装就是使用private去定义变量,通过方法get/set去调用。参考书本的92页。
-
static方法只能处理static数据
当需要引用或修改一个static限定的类属性时,可以使用类名也可以使用对象名。(访问的是同一个内存单元)
-
new对象后面的(),没东西就是无参构造方法。
子类不继承父类的构造方法,如果子类构造方法中没有指明调用哪一个父类构造方法,将会自动调用父类无参构造,如果要使用有参需要在super的括号中写入参数名。
多态
- 多态是方法的多态,属性没有多态。
- 存在的条件:继承关系、方法重写、父类引用指向子类对象 Father f1 = new Son();
- static/final/private没有多态。
对象的继承
对象instanceof类,如果这个对象是这个类或者它的子类创造的,就可以得到true。
对象的上转型对象:
Animal a;(Animal类是Tiger类的父类)
a = new Tiger();(称a是b的上转型对象)
1.上转型对象不能操作子类新增的东西。(所以要加上子类名字并括号,类似强制转换。)
2.上转型对象调用的只会是子类没重写的或者重写后的。
抽象类
1.抽象类不能new,只能靠子类去实现。
2.抽象方法是没有方法内容的,只有声明。
3.抽象方法必须在抽象类里,抽象类不一定有抽象方法。
4.子类重写父类的抽象方法。
接口
1.定义一些方法,让不同的人实现。
2.默认方法前缀public abstract
3.默认常量前缀public static final
4.接口不能被实例化,接口中没有构造方法
5.implements可以实现多个接口
6.抽象类可以不重写直接用。
7.接口与存储空间无关,因为不包含任何实现。
抽象类中的非抽象方法不用重写,其他必须重写,接口的方法必须重写,接口和抽象类中只有方法名,没有定义的,如果你不定义 也就是空方法,接口就是为了弥补java不能多重继承,接口针对的是对象而不是实现。实现的部分可以交由对象去实现。这就是java中的多态啊。好好思考一下上面的几句话就行了。一定要对java的基本理论清楚。
接口回调类似上转型对象。
匿名
使用一般形式传参:
Scanner sc = new Scanner(System.in);
int num = sc.nextInt();
使用匿名对象传参:
int num = new Scanner(System.in).next.Int();
System.out.println(“输入的是:”+num)
1.匿名的是只用一次的,所以直接new。
异常
异常的捕获是层层递进的,如果在上面已经捕获了一个大的异常,那么下方无法捕获大异常内的小异常。并且,不要用子类去捕获父类的异常,尽量用一个父类去捕获所有子类的异常。
Label类即创建一个标签,用于显示一些不需要修改的文本信息。
actionListener接口监听和处理TextFiled对象的事件
根据程序的构成和运行环境的不同,Java源程序分为两大类:Application程序和Applet程序。
GUI
JLabel 标签,显示在最底层的文字
JButton按钮
JPanel面板,把组件加进去
多线程
自定义一个类,作为线程类继承Thread类,用于创建线程。
重写run()方法,run()是线程的执行体。
创建线程对象后,调用自带的start()方法启动线程,观察多线程机制。
注意点
new了Thread就等于是一个新线程了,race只是一个对象,一个平台,去运行线程。
两种多线程方式
不涉及多个线程操作一个对象就可以只Thread
继承Thread类
- 子类继承,所以具备多线程能力
- 启动线程:子类对象.start()
- 不建议使用:因为要避免OOP单继承局限性
public class TestThread1 extends Thread{
@Override
public void run() {
//run方法线程体
for(int i=0;i<20;i++){
System.out.println("我在看片--"+i);
}
}
public static void main(String[] args){
//主线程
//创建线程
TestThread1 t1 =new TestThread1();
//开始线程
t1.start();
for(int i =0;i<2000;i++) {
System.out.println("我在听歌--"+i);
}
}
}
实现Runable接口
- 实现接口Runable,使具有多线程能力
- 启动线程:传入目标对象+Thread对象.start()
- 推荐使用:因为它灵活,方便同一个对象被多个线程使用
package com.sun.多线程;
public class TestThread2 implements Runnable{
@Override
public void run() {
//run方法线程体
for(int i = 0;i < 20; i++){
System.out.println("我在看片--"+i);
}
}
public static void main(String[] args){
//主线程
//创建线程
TestThread2 t1 = new TestThread2();
//开始线程
new Thread(t1).start();
for(int i=0;i<2000;i++){
System.out.println("我在听歌--"+i);
}
}
}
并发问题
多个线程操作同一个资源可能引发线程不安全,数据紊乱。
线程休眠sleep
- sleep(time)指当前吸纳成阻塞的毫秒数。
- sleep存在异常interrupedException。
- sleep时间到了之后,线程进入就绪状态。
- sleep可以模拟网络延时,倒计时等等。
- 每一个对象都有一个锁,sleep不会释放锁。
- sleep可以放大问题的发生性
线程礼让yield
- 礼让线程,让当前正在执行的线程暂停,但不阻塞。
- 讲线程从运行状态转为就绪状态。
- cpu重新调度,但是让不让还是看cpu看谁顺眼。
线程强制执行join
- join合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞
- 类似插队
线程五大态
-
NEW
尚未启动的线程
-
RUNNABLE
在JVM里执行的线程
-
BLOCKED
被阻塞等待监视器锁定的线程
-
WAITING
正在等待另一个线程执行操作的线程
-
TIMED_WAITING
正在等待另一个线程执行操作到了一定时间的线程
-
TERMINATED
已经退出(死翘翘)的线程
优先级
通过getPriority()获取当前优先级
通过setPriority(int xx)改变当前优先级
守护线程
setDaemon(ture)通过这个方法将他定义为守护线程。
线程同步方法
synchronized
两种使用方式,一种是在方法声明里加入synchronized
class BuyTicket implements Runnable{
public void buy(){
xxx.xxx;
}
}
//变为
class BuyTicket implements Runnable{
public synchronized void buy(){
xxx.xxx;
}
}
但是在方法中直接插入synchronized字段的方式不可取,因为synchronized锁定的线程是他所在的对象,
如上例,锁定的是class BuyTicket{}
第二种方式
synchronized(obj){}
//同步前。
package com.sun.多线程;
public class BankDrawing {
public static void main(String[] args) {
Account account = new Account(1000,"钱");
Drawing I = new Drawing(account,800,"我");
Drawing you = new Drawing(account,600,"你");
you.start();
I.start();
}
}
class Account {
int money;
String name;
public Account(int money, String name){
this.money = money;
this.name = name;
}
}
class Drawing extends Thread{
Account account;//账户
String name;
int drawingMoney;//取钱
int nowMoney;//手里的钱
public Drawing(Account account, int drawingMoney, String name){
super(name);
this.account = account;
this.drawingMoney =drawingMoney;
}
@Override
public void run(){
//判断有没有钱
if(account.money-drawingMoney<0){
System.out.println(Thread.currentThread().getName()+"钱不够,取不了");
return;
}
account.money = account.money-drawingMoney;
nowMoney = nowMoney+drawingMoney;
System.out.println(Thread.currentThread().getName()+"取了"+drawingMoney);
System.out.println("卡里还剩"+account.money);
System.out.println(Thread.currentThread().getName()+"手里有"+nowMoney);
}
}
//同步后。
package com.sun.多线程;
public class BankDrawing {
public static void main(String[] args) {
Account account = new Account(1000,"钱");
Drawing I = new Drawing(account,800,"我");
Drawing you = new Drawing(account,600,"你");
you.start();
I.start();
}
}
class Account {
int money;
String name;
public Account(int money, String name){
this.money = money;
this.name = name;
}
}
class Drawing extends Thread{
Account account;//账户
String name;
int drawingMoney;//取钱
int nowMoney;//手里的钱
public Drawing(Account account, int drawingMoney, String name){
super(name);
this.account = account;
this.drawingMoney =drawingMoney;
}
@Override
public void run(){
synchronized(account){
//判断有没有钱
if(account.money-drawingMoney<0){
System.out.println(Thread.currentThread().getName()+"钱不够,取不了");
return;
}
account.money = account.money-drawingMoney;
nowMoney = nowMoney+drawingMoney;
System.out.println(Thread.currentThread().getName()+"取了"+drawingMoney);
System.out.println("卡里还剩"+account.money);
System.out.println(Thread.currentThread().getName()+"手里有"+nowMoney);
}
}
}
使用synchronized(obj){}将代码块包裹住,让他不会作用于本身。
lock锁
lock锁只能锁代码块。而且需要手动开启和关闭锁(即.lock和.unlock)
与synchronized不同之处在于,synchronized是隐式的,而且可以锁方法,并且自动释放。
class X{
//通过ReentrantLock定义锁对象
private final ReentrantLock lock=new ReentrantLock();
//定义需要保证线程安全的方法
public void m() {
//加锁
lock.lock();
try {
//需要保证线程安全的代码
}
finally {
//关闭/释放
lock.unlock();
}
}
}
线程通信
- wait(), 表示线程一直等待,直到其他线程通知,与sleep不同,会释放锁。
- notify() ,唤醒一个处于等待状态的线程
- notifyAll(), 唤醒同一个对象上所有调用wait()方法的线程,优先级高的线程优先调度
管程法
采用并发协作模型。
//管程法解决生产者消费者问题
public class TestPC {
public static void main(String[] args) {
SynContainer container = new SynContainer();
new Productor(container).start();
new Consumer(container).start();
}
}
//生产者
class Productor extends Thread{
SynContainer container;
public Productor(SynContainer container) {
this.container = container;
}
//生产
@Override
public void run() {
for (int i = 0; i < 100; i++) {
container.push(new Chicken(i));
System.out.println("生产了"+i+"只鸡");
}
}
}
//消费者
class Consumer extends Thread{
SynContainer container;
public Consumer(SynContainer container) {
this.container = container;
}
//消费
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("消费了-->"+container.pop().id+" 只鸡");
}
}
}
//产品对象
class Chicken{
int id;
public Chicken(int id) {
this.id = id;
}
}
//缓冲区
class SynContainer{
//容器大小
Chicken[] chickens = new Chicken[10];
//容器计数器
int count = 0;
//生产者放入产品
public synchronized void push(Chicken chicken) {
//如果容器满了,需要等待消费者消费
if(count == chickens.length) {
//通知消费者消费,生产等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//没有满,需要丢入产品
chickens[count] = chicken;
count++;
//通知消费者消费
this.notifyAll();
}
//消费者消费产品
public synchronized Chicken pop() {
//判断能否消费
if(count == 0) {
//等待生产者生产,消费者等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//可以消费
count--;
Chicken chicken = chickens[count];
//吃完了,通知生产者生产
this.notifyAll();
return chicken;
}
}
信号量法
//生产者消费者方案2:信号灯法,标志位解决
public class TestPC2 {
public static void main(String[] args) {
TV tv = new TV();
new Player(tv).start();
new Wathcer(tv).start();
}
}
//生产者--->演员
class Player extends Thread{
TV tv;
public Player(TV tv) {
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
if(i%2==0) {
this.tv.play("快乐大本营");
} else {
this.tv.play("douyin");
}
}
}
}
//消费者--->观众
class Wathcer extends Thread{
TV tv;
public Wathcer(TV tv) {
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
tv.watch();
}
}
}
//产品 ---> 节目
class TV {
//演员表演,观众等待 T
//观众观看,演员等待 F
String voice; //表演的节目
boolean flag = true;
//表演
public synchronized void play(String voice) {
if(!flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("演员表演了:"+voice);
//通知观众观看
this.notifyAll(); //通知唤醒
this.voice = voice;
this.flag = !this.flag;
}
//观看
public synchronized void watch() {
if(flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("观看了:"+voice);
//通知演员表演
this.notifyAll();
this.flag = !this.flag;
}
}
反射
JVM
方法区:存放类字节码、常量、静态变量
Java堆:存放对象实例
Java栈:方法执行
Java启动JVM,JVM 的类加载器根据 Java 命令的参数到指定的路径加载.class 类文件。
类文件加载到内存后,放到专门的方法区。
JVM创建一个主线程执行这个类文件的main方法,压入Java栈运行。
方法里如果有static对象,那这个对象会存在Java堆里面,包括它的变量信息,而对象在Java堆里的地址存在Java栈里面
main方法里进入其他方法的时候,会再新建一个栈放入Java栈,运行结束就出栈。