1、自增变量
public static void main(String[] args) {
int i = 1;
i = i++;
int j = i++;
int k = i + ++i * i++;
System.out.println("i=" + i);
System.out.println("j=" + j);
System.out.println("k=" + k);
}
执行i = i ++; 先将i变量压入操作数栈,然后在对i变量进行自增,最后吧计算结果复制给i,i任然是1
i++计算后将值复制给j
最后一步
2、单例模式
单例设计模式,即某个类在整个系统中只能有一个实例对象可被获取和使用的代码模式
例如:代表JVM运行环境的Runtime类
2.1、要点:
- 某个类只能有一个实例
- 构造器私有化
- 必须自行创建实例
- 含有一个该类的静态变量来保存这个唯一实例
- 必须自行像整个系统提供这个实例
- 对外提供获取该类实例对象的方式
-
直接暴露
-
用静态变量的get方法获取
-
- 对外提供获取该类实例对象的方式
2.2几种常见形式:
- 饿汉式:直接创建对象,不存在线程安全问题
- 直接实例化饿汉式(简洁直观)
- 枚举(最简洁)
- 静态代码块饿汉式(适合复杂实例化)
- 懒汉式:延迟创建对象
- 线程不安全(适用于单线程)
- 线程安全(适用于多线程)
- 静态内部类模式(适用于多线程)
饿汉式:
- 直接实例化
public class singleton1{
private singleton1(){
}
public static final Singleton1 INSTANCE = new Singleton1();
}
- 枚举
public enum Singleton2{
/**
* 枚举类型:表示该类型是有限的几个
*/
INSTANCE
}
- 静态代码块
public class Singleton3{
public static final Singleton3 INSTANCE;
private String info;
static{
try{
INSTANCE = new Singleton3("123")
}catch(Exception e){
e.printstackTrace();
thorw new RuntimeException(e);
}
}
private Singleton3(String info){
this.info = info;
}
}
懒汉式:
- 线程不安全
public class Singleton4{
private static Singleton4 instance;
private Singleton4(){}
public static Singleton4 getInstance(){
if(instance == null){
instance = new Singleton4();
}
return instance;
}
}
- 线程安全
public class Singleton5{
private static volatile Singleton5 instance;
private Singleton5(){}
public static Singleton5 getInstance(){
//第一层lock
if(instance == null){
//第二层
sychronized(Singleton5.class){
if(instance == null){
instance = new Singleton5();
}
}
}
return instance;
}
}
- 静态内部类
public class Singleton6{
private Singleton6(){}
public static class Inner{
private static final Singleton6 INSTANCE = new Sinleton6();
}
public static Singleton6 getInstance(){
return Inner.INSTANCE;
}
}
3、类初始化实例初始化
3.1、类初始化
- 一个类要创建实例需要先加载并初始化该类
- main方法所在的类需要先加载和初始化
- 一个子类要初始化需要先初始化父类
- 一个类初始化就是执行 clinit 方法
- clinit 方法由静态类变量显示赋值代码和静态代码块组成
- 类变量显示赋值代码和静态代码块代码从上到下执行
- linit 方法只调用一次
3.2、实例初始化
- 实例初始化就是执行 init() 方法
- init () 方法可能重载有多个,有几个构造器就有几个 init() 方法
- init() 方法由非静态实例变量显示赋值代码和非静态代码块,对应构造器代码组成
- 非静态实例变量显示赋值代码和非静态代码块从上到下顺序执行,而对应构造器的代码最后执行
- 每次创建实例对象,调用对应构造器,执行的就是对应的 ini方法
- init 方法的首行是super()和super(实参列表) ,即对应父类的 init 方法
package com.atguigu.classLoader;
/**
* 父类初始化<clinit>
* 1、j = method()
* 2、 父类的静态代码块
*
* 父类实例化方法:
* 1、super()(最前)
* 2、i = test() (9)
* 3、子类的非静态代码块 (3)
* 4、子类的无参构造(最后)(2)
*
*
* 非静态方法前面其实有一个默认的对象this
* this在构造器或<init> 他表示的是正在创建的对象,因为咱们这里是正在创建Son对象,所以
* test()执行的就是子类重写的代码(面向对象多态)
*
* 这里i=test() 执行的就是子类重写的test()方法
*
*/
public class Father {
private int i = test();
private static int j = method();
static{
System.out.println("(1)");
}
Father() {
System.out.println("(2)");
}
{
System.out.println("(3)");
}
public int test(){
System.out.println("(4)");
return 1;
}
public static int method() {
System.out.println("(5)");
return 1;
}
}
package com.atguigu.classLoader;
/**
* 子类的初始化<clinit>
* 1、j = method()
* 2、子类的静态代码块
*
* 先初始化父类 (5)(1)
* 初始化子类 (10) (6)
*
* 子类实例化方法:
* 1、super()(最前
* 2、i = test() (9)
* 3、子类的非静态代码块 (8)
* 4、子类的无参构造(最后)(7)
*/
public class Son extends Father {
private int i = test();
private static int j = method();
static {
System.out.println("(6)");
}
Son() {
super();
System.out.println("(7)");
}
{
System.out.println("(8)");
}
public int test(){
System.out.println("(9)");
return 1;
}
public static int method() {
System.out.println("(10)");
return 1;
}
public static void main(String[] args) {
Son son = new Son();
System.out.println();
Son son1 = new Son();
}
}
执行结果 (5) (1) (10) (6) (9) (3) (2) (9) (8) (7)
(9) (3) (2) (9) (8) (7)
4、方法参数传递机制
package com.atguigu.methodParam;
import java.util.Arrays;
/**
* @author gcq
* @Create 2020-09-28
*/
public class Exam4 {
public static void main(String[] args) {
int i = 1;
String str = "hello";
Integer num = 200;
int[] arr = {1,2,3,4,5};
MyData my = new MyData();
change(i,str,num,arr,my);
// arr my变了
System.out.println("i= " + i);
System.out.println("str= " + str);
System.out.println("num= " + num);
System.out.println("arr= " + Arrays.toString(arr));
System.out.println("my.a= " + my.a);
}
public static void change(int j, String s, Integer n, int[] a, MyData m) {
j += 1;
s += "world";
n += 1;
a[0] += 1;
m.a += 1;
}
}
class MyData {
int a = 10;
}
考点:
- 方法的参数传递机制
- String、包装类等对象的不可变性
4.1、方法的参数传递机制
- 形参是基本数据类型
- 传递数据值
- 实参是引用数据类型
- 传递地址值
- 特殊的类型:String、包装类等对象的不可变性
输出结果
i= 1
str= hello
num= 200
arr= [2, 2, 3, 4, 5]
my.a= 11
5、成员变量和局部变量
考点:
- 就近原则
- 变量的分类
- 成员变量
- 类变量,实例变量
- 局部变量
- 非静态代码的执行:每次创建实例对象都会执行
- 方法的调用规则:调用一次执行一次
5.1、局部变量与成员变量区别
- 1、声明的位置
- 局部变量:方法体{}中,形参,代码块{}中
- 成员变量:类方法外
- 类变量:static修饰
- 实例变量:没有static修饰
- 2、修饰符
- 局部变量:final
- 成员变量:public protected private final static volatile transient
- 3、值存储位置
- 局部变量:栈
- 实例变量:堆
- 类变量:方法区
- 堆(Heap) ,此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。这一点在Java虚拟机规范中的描述是:所有的对象实例以及数组都要在堆上分配。
- 通常所说的栈(Stack) ,是指虚拟机栈。虚拟机栈用于存储局部变量表等。局部变量表存放了编译期可知长度的各种基本数据类型(boolean、byte、 char、short、 int、 float、long、double) 、对象引用(reference 类型,它不等同于对象本身,是对象在堆内存的首地址)。方法执行完, 自动释放。
- 方法区(Method Area)用于存储已被虛拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
- 4、作用域:
- 局部变量:从生命出开始,到}结束
- 实例变量:从当前类中this,有时this可以省略,在其他类中对象名.访问
- 类变量:在当前类中类名,有时类名可以省略,在其他类中类名.或对象名.访问
- 5、生命周期
- 局部变量:每个线程,每一次调用都是执行新的生命周期
- 实例变量:随着对象的创建而初始化,随着对象的被回收而消亡,每一个对象的实例变量都是独立的。
- 类变量:随着类的初始化而初始化,随着类的卸载而消亡,该类的所有对象的类变量是共享的
- 当局部变量与XX变量重名时,如何区分
- 局部变量与实例变量重名时,在成员变量前加this
- 局部变量与类变量重名时,在类变量前面加类名
public class Exam5 {
static int s;// 5
int i; // A-2 B-1
int j;//A-1 B-1
{
int i = 1;
i++; // 就近原则
j++;
s++;
}
public void test(int j) {
j++; // 就近原则 21
i++;
s++;
}
public static void main(String[] args){
Exam5 obj1 = new Exam5();
Exam5 obj2 = new Exam5();
obj1.test(10);
obj1.test(20);
obj2.test(30);
// 2 1 5
System.out.println(obj1.i + "," + obj1.j + "," + obj1.s);
// 1 1 5
System.out.println(obj2.i + "," + obj2.j + "," + obj2.s);
}
}