堆
栈
方法区 :存方法,静态数据
匿名对象
没有变量名的对象 匿名对象
new Car();
this 代表本类对象,代表它所在函数对象的引用。哪个对象在调用this所在的函数,this就代表哪个对象
构造函数
在构造函数相互调用的时候只能用 this(参数 ) 并且要在第一行
静态 static 不能修饰局部变量,随类的加载而加载
可以让对象调用也可以用类名调用
静态代码块 随着类的加载而执行,只执行一次 一般给类初始化
static{
}
构造代码块,在默认构造函数之后执行,在调用相应的构造函数之前执行
{
}
final
可修饰类 方法 变量
修饰的类不能继承
修饰的方法不能覆盖
修饰的变量是一个常量,只能赋值一次
内部类只能访问被final修饰的局部变量
抽象类 abstract 可以有抽象方法和普通方法
抽象方法一定在抽象类中
抽象类不能new对象
如果子类只复写抽象类的部分抽象方法,子类还是抽象类
对象实例 instanceof 类型
多态
成员函数特点
成员函数在多态调用的时候,编译看左边, 运行看右边
注意:静态成员函数在多态调用的时候看左边
例:
Fu f = new Zi();
f.fun();//当 fun 是static方法 这样访问的时候 会直接访问父类的方法
这是因为 这是静态方法可以通过类名直接访问 而左边 恰恰就是父类的 类名
在多态中,成员变量的特点
无论编译和运行,都参考左边(引用型变量所属的类)
Fu f = new Zi();
f.num;//这里调用的是父类的num
内部类
创建内部类对象
Outer.Inner in = new Outer().new Inner();
一个类可以私有
内部类可以作为其他类的私有成员
class Outer{
private int x = 3;
class Inner{
int x = 5;
//this.x 内部类的 x
//Outer.this.x 外部类的 x
}
}
静态内部类 只能直接访问外部类的static成员
创建静态内部类
Outer.Inner in = new Outer.Inner();
in.func();//访问静态内部类的非静态成员
//访问静态内部类的静态成员
Outer.Inner.func();
静态内部类无法从外部类引用 非静态成员,只能访问 外部类的 静态成员
非静态内部类不能声明 静态成员,如果内部类是静态的 类成员必须是静态的
外部类的静态成员函数 不能访问非静态内部类,只能访问静态内部类
内部类定义在局部的时候(就是把内部类定义在外部类的方法里面)
1、不可以被成员修饰符修饰(public private …)
2、可以直接访问外部类中的成员,因为还持有外部类的引用
但是不可以访问它所在的局部中的变量,除非该局部变量是用 final 修饰的局部变量
3、内部类定义在局部不能用static。
匿名内部类:
1、匿名内部类其实就是内部类的简写格式
2、定义匿名内部的前提:内部类必须是继承一个类或者实现接口
3、匿名内部类的格式:new 父类或者接口(){定义子类内容}
4、
例:
abstract AbsDemo{
abstract void show();
}
class Outer{
public void func(){
//匿名内部类
new AbsDemo{
void show(){
System.out.println("show");
}
}.show();
}
}
面试题
若一个想创建一个内部类简单的运行一个方法这个内部类 没有父类 也没有接口
解:用Object类 的匿名内部类
new Object(){
void func(){
}
}.func();
异常
throws和throw的区别
throws使用在函数上 throws后面跟异常类,可以跟多个,用逗号分开
throw使用在函数内 throw 后面跟的是异常对象
Exception 中有一个特殊的子异常RuntimeException 运行时异常
如果在函数内抛出该异常,函数上可以不用声明,编译一样通过(直接用throw 不用 在函数上用throws声明)
如果在该函数上声明该一样,调用者不用try-catch 处理 编译一样通过
自定义异常时 如果该异常的发生,无法再继续进行运算就让自定义异常继承RuntimeException
多线程
创建新线程有三中方法
一、继承Thread类创建线程类
(1)定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。因此把run()方法称为执行体。
(2)创建Thread子类的实例,即创建了线程对象。
(3)调用线程对象的start()方法来启动该线程。让虚拟机去调用线程的 run(), 如果用户直接调用run() 方法并不会有新的线程
public class FirstThreadTest extends Thread{
int i = 0;
//重写run方法,run方法的方法体就是现场执行体
public void run()
{
for(;i<100;i++){
System.out.println(getName()+" ---- "+i);
}
}
public static void main(String[] args)
{
for(int i = 0;i< 100;i++)
{
System.out.println(Thread.currentThread().getName()+" : "+i);
if(i==20)
{
new FirstThreadTest().start();
new FirstThreadTest().start();
}
}
}
}
上述代码中Thread.currentThread()方法返回当前正在执行的线程对象。GetName()方法返回调用该方法的线程的名字。
二、通过Runnable接口创建线程类
(1)定义runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
(2)创建 Runnable实现类的实例,并依此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
(3)调用线程对象的start()方法来启动该线程。
示例代码为:
public class RunnableThreadTest implements Runnable
{
private int i;
public void run()
{
for(i = 0;i <100;i++)
{
System.out.println(Thread.currentThread().getName()+" "+i);
}
}
public static void main(String[] args)
{
for(int i = 0;i < 100;i++)
{
System.out.println(Thread.currentThread().getName()+" "+i);
if(i==20)
{
RunnableThreadTest rtt = new RunnableThreadTest();
new Thread(rtt,"thread1").start();
new Thread(rtt,"thread2").start();
}
}
}
}
三、通过Callable和Future创建线程
(1)创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。
(2)创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。
(3)使用FutureTask对象作为Thread对象的target创建并启动新线程。
(4)调用FutureTask对象的get()方法来获得子线程执行结束后的返回值
实例代码:
public class CallableThreadTest implements Callable<Integer>
{
public static void main(String[] args)
{
CallableThreadTest ctt = new CallableThreadTest();
FutureTask<Integer> ft = new FutureTask<>(ctt);
for(int i = 0;i < 100;i++)
{
System.out.println(Thread.currentThread().getName()+" 的循环变量i的值"+i);
if(i==20)
{
new Thread(ft,"有返回值的线程").start();
}
}
try
{
System.out.println("子线程的返回值:"+ft.get());
} catch (InterruptedException e)
{
e.printStackTrace();
} catch (ExecutionException e)
{
e.printStackTrace();
}
}
@Override
public Integer call() throws Exception
{
int i = 0;
for(;i<100;i++)
{
System.out.println(Thread.currentThread().getName()+" "+i);
}
return i;
}
}
创建线程的三种方式的对比
采用实现Runnable、Callable接口的方式创见多线程时,优势是:
线程类只是实现了Runnable接口或Callable接口,还可以继承其他类。
在这种方式下,多个线程可以共享同一个target对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU、代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。
劣势是:
编程稍微复杂,如果要访问当前线程,则必须使用Thread.currentThread()方法。
使用继承Thread类的方式创建多线程时优势是:
编写简单,如果需要访问当前线程,则无需使用Thread.currentThread()方法,直接使用this即可获得当前线程。
劣势是:
线程类已经继承了Thread类,所以不能再继承其他父类。
getName() 获取线程名称
setName() 设置线程名称(构造函数通过super(“xxx”) 也能设置名字)
同步代码块
synchronized(对象){//对象是所需要的锁 没有特殊情况 一般用 this 锁
需要同步的代码
}
同步函数 ,同步函数函数的锁用的是this 锁,
若同步函数是静态的(静态进内存时 内存中没有本类对象 但一定有该类对应的字节码文件对象) ,类名.class 该对象的类型是Class
静态的同步方法 使用的锁是该方法所在类的字节码文件对象。类名.class
当在静态方法中需要写同步代码块 需要用 类名.class 的锁
public synchronized void add(int i){
}