第①模块.面向对象基本知识点
1.类
关键字package
java.lang包下包含一些Java语言的核心类如String Math Integer System Thread 等
java.util包下包含一些工具类 如集合框架类 日期日历的相关函数
关键字import
如果使用的类或者接口是本包下定义的 那么可以省略掉import
如果使用的类和接口是java.lang包下定义的则可以省略import
如果在一个类中使用了不同包下的同名的类 那么至少有一个用全类名的方式使用
1.类及类的成员: 属性 方法 构造器 代码块 内部类
2.类可以使用 final abstract static(只能在内部类中定义)
-
final修饰类 该类不允许被继承
-
abstract修饰类是抽象类 该类不能被实例化只能被子类继承 **(就相当于一个接口) **抽象类中可以定义非抽象方法(具有方法体的方法),但是非抽象类中不能定义抽象方法(无方法体的方法)
//无方法体的方法就是 没有大括号 void method(...); //有方法体的方法 不管大括号里写不写内容 这也算是有方法体 void method(...){};
-
static修饰的内部类 可以使用外部类.内部类来实例化或调用其属性
2.对象的属性默认初始化赋值
引用数据类型(String 数组 等) 类型默认是 null
int byte short long 默认是0
float double 默认0.0
boolean 默认值 false
char 默认空字符 ' '
3.类中的成员变量与局部变量
1、相同点:都有作用域
2、不同点:
1.成员变量可以使用 权限修饰符、static、final、volatile关键字修饰 |||||| 局部变量不可以(局部变量 -只能- 使用一个修饰符–final声明成常量 即在声明的时候必须赋值)
2.成员变量在堆中(非static) 局部变量在栈中
class Person{
// 成员变量 直接定义在类中{}内的变量 可以不赋值 创建对象是有默认的初始化
public String name;
private int age;
/* 方法内部 方法参数 构造器内部 构造器参数 代码块内部 定义的变量都属于局部变量 没有默认初始化 必须显示赋值才能使用 特别的:形参在调用时赋值*/
// 1、方法内部的变量
void method1(){
String name;
int age;
System.out.println(name+" "+age); // 报错 没有赋值就使用
}
///2、方法的参数 就是说在调用方法的时候必须传参 就相当给形参赋值 内部可以使用
void method2(String name,int age)){
}
// 3、构造器内部的变量
Person(){
String name;
int age;
System.out.println(name+" "+age); // 报错 没有赋值就使用
}
// 4、构造器形参 调用构造器时必须传参
Person(String name,int age){
}
// 5、代码块内部的变量
{
String name;
int age;
System.out.println(name+" "+age); // 报错 没有赋值就使用
}
}
各种变量及对象存放的位置
class A{
Object obj1 = new Object(); // 类的变量 obj1会在创建对象的时候分配在堆空间中
static Object obj2 = new Object(); //静态的变量 jdk6及之前在方法区 jdk7之后静态变量也在堆空间中
final Object obj3 = new Object(); //final修饰的常量 只意味着obj3这个变量是常量 这个变量只能引用这个对象 不能指向其他的对象了 final类型的成员变量也在堆空间
static final Object obj4 = new Object(); // 结合obj2 obj3 分析可得jdk6及之前在方法区 jdk7之后静态变量也在堆空间中
void method1(){
Object obj = new Object(); // 方法里面的局部变量 存在栈中的局部变量表里
}
}
那么变量引用的对象存在哪呢?
只需记住一点 : // new出来的对象本身一定在堆中
属性的赋值方式 :JVM笔记
4.类的方法及return关键字
1.方法可以使用 权限修饰符 final abstract static 来修饰
- final修饰的方法不能被重写
- abstract的修饰方法 不能有方法体
- static的修饰方法 属于类方法 直接类名.调用 加载到方法区
2.返回值是void的方法 可以写 return;代表结束此方法的意思 必须直接加分号
3.方法中可以调用当前类的属性或方法 也可以调自己(递归) 但是不能再次定义方法(方法套方法)
4.在一个方法中 不管怎样执行 只要执行到了return语句 那么方法就结束
>方法的重载
在一个类中允许存在一个以上的同名的方法 只要他们的参数个数或者参数类型不同即可
跟 权限修饰符 返回值类型 形参变量名 方法体 没关系 只关注方法名 和 参数列表
只要方法名一样 参数列表不同 就是重载
public void say(){}
private void say(){} // 报错 冲突 不是重载 跟权限修饰符无关
public String say(){ // 报错 冲突 不是重载 跟返回值类型无关
return null;
}
public String say(String name){ // 算重载
return null;
}
void say(String name){};
void say(String nn){}; // 报错 数据类型和个数和第一个一样
void say(String name,int age){};
void say(String nn,int aa){}; // 报错 数据类型 数据个数 顺序 和第三个一样
void say(int age,String name){}; // 不报错 参数数据类型的顺序和3不一样
>可变个数形参的方法
1.使用方式 数据类型 ... 变量名 就相当是数组 调用的时候
public void test(String ... strs){
strs.length;
}
// 调用
test(); //可以不传参
test("ssd");
test("ss","asd","sdd");
test(new String[]{"as","sdss","xxx"}) //传一个数组
2.当调用可变个数形参的时候 传入的参数可以是0 1 2 …
3.可变参数的方法可以与同名方法 形参不同的方法构成重载
public void test(String ... strs){}
public void test(int age){} //可以构成重载
4.与类型相同的数组不能构成重载
public void test(String ... strs){}
public void test(String[] strs){} //报错重复 不能构成重载
public void test(int ... nums){}
public void test(int[] nums){} //报错重复 不构成重载
、、、
5.有可变参数的方法 可变参数**必须**放在末尾
public void test(int num ,String ... strs){}
public void test(String str,String ... strs){}
public void test(String ... strs,String str){} //报错
// test("a","sd","sdsd");如果出现这种情况 编译器分不清参数都是谁的
>方法参数的值传递机制
Java里参数传递方式只有一种——>值传递
1.如果形参是基本类型数据,将实参的 ==数据值==传递给了形参
2.如果形参是引用数据类型,是将实参引用的对象在堆空间的 地址值传递给了形参
https://www.zhihu.com/question/31203609
>递归方法的调用
1.递归求和
int getSum(int n){
if(n==1){
return 1;
}else{
return n + getSum(n-1)
}
}
如果输入 n = 4
那么就是 连着调用 然后依次返回值
1、第一次 判断n=4 这时程序想要返回 4 + getSum1(3)
2、然后再次调用此方法 3 + getSum(2)
3、然后再次调用此方法 getSum(2) = 2 + getSum(1)
4、再次调用此方法这时 n = 1 直接求出 getSum(1) = 1
调用结束时 从步骤四开始返回值 返回给步骤三 getSum(1) = 1 这时步骤4的方法就执行结束
所以步骤三得到步骤二的返回值getSum(1) = 1 所以计算出 getSum(2) = 3 返回值给步骤二
步骤二经计算 得到6 返回给步骤一 步骤二方法结束
步骤1得到返回值计算 6 + 4 = 10 方法结束 返回结果
递归调方法时是 A 调 A1 A1调A2 ... 然后从最后一个被调的方法开始接连往前返回值 最后返回最终结果
方法结束
5.构造器
作用:只是初始化对象
构造器不能被 static final synchronized abstract native 修饰 不能有return语句 可以使用四种权限修饰符 默认无参的构造器权限看类的权限
构造器的重载 定义的所有构造器都是重载
默认有空参的构造器 如果我们自己定义了构造器 那么默认的空参构造器就没有了
子类的所有构造器默认调用父类无参的构造器 如果父类中没有无参的构造器 那么在子类的构造器中必须显示的调用父类有参的构造器,并且必须放在第一行
Object obj = new Object();
这行代码我们可以看
1.Object 表示类信息
2.obj 表示引用
3.new 在堆中开辟空间 根据前面的类信息创建一个Object类的对象(分配内存空间 对属性默认初始化)
4.Object() 表示调用类的构造器 初始化对象 <init>
关于protected和private修饰构造器的不同
1.protected修饰一个类的构造器 这个类可以有子类 private修饰一个类的构造器 这个类不能有子类
// protected修饰一个类的构造器 那么这个类没有子类
class Dad{
private Dad(){ //如果只有一个私有的无参构造器 那么这个类没有子类
}
}
class Person{
Person(){
super(); // 因为子类的构造器默认在第一行调用父类的无参构造器中 如果父类的构造器是私有的 就调不来了,也就无法创建子类对象 也即无法创建匿名内部类
}
}
//***************** protected修饰构造器 可以有子类继承
class Dad{
protected Dad(){ //如果只有一个受保护的无参构造器 那么这个类可以有子类
}
}
class Person{
Person(){
super(); //受保护的即只能在本类 一个包 或者子类中使用 那么这里调父类的构造器就可以调 即可以创建子类的对象 也就是protected修饰的构造器可以为其创建匿名内部类
}
6.static关键字
只能修饰一个类中的:属性
、方法
、代码块
、内部类(只能修饰内部类)
静态修饰结构在类加载的时候就已经加载,所以只会在内存中存在一份,因为类只加载一次 可以通过类名来调用
非静态的方法可以调静态方法和使用静态属性,静态方法不能调非静态的属性和方法 因为静态的结构先于非静态结构加载 静态方法里调静态属性,默认前面是类名,因为静态方法里没有this
在开发中,如何确定一个属性是否要声明为static?
属性是可以被多个对象所共享的,不会随着对象的不同而不同的
代码块及静态代码块
1、代码块的作用:用来初始化
2、只能使用static修饰(静态代码块)
3、代码块都是自动执行的 外部无法调用
4、静态代码块:
①静态代码块在类加载的时候执行 ,因为类只加载一次,所以静态代码块只执行一次 它 的作用就是初始化类的信息
②内部只能调用静态的属性 方法
③作用: 初始化类的信息 主要就是为静态属性赋值 还有就是一些大的资源(如数据库连接池等要用单例模式 但是在创建之前还要进行一些列的操作 这是就使用静态代码块先做操作后创建对象 视频P329)
5、非静态代码块
①而非静态代码块每new一个对象就会执行一次 它的作用是初始化属性 比构造器先执行
②非静态代码块可以调用静态的属性,方法,或者非静态的属性 方法
③作用:为属性进行初始化
③在构造器前执行 只要调了构造器就会执行非静态代码块
在new一个子类对象时 下面的执行顺序
父类的静态代码块 非静态代码块 构造器 子类的静态代码块 非静态代码块 构造器
由父及子 静态先行
- 第一阶段 类加载阶段
创建子类对象时先去检查父类是否加载 没有加载先加载父类 这时调用父类的静态代码块 然 后再去加载子类,执行子类的静态代码块
-
第二阶段 创建对象阶段
当子类和父类都加载完毕后,当我们 Object obj = new Object();时,调的就是子类的构造器,
但是子类的构造器默认首行调用父类的构造器,我们又知道,一个类在创建对象是非静态代码块
先与构造器执行,所以这时就是父类的代码块先执行,然后执行父类的构造器,最后才轮到子类,
即子类的非静态代码块,子类的构造器
所以我们记住 由父先子 静态先行
属性的加载赋值位置和顺序
有如下代码 表示出属性的加载及赋值顺序(非静态)
class A{
int num = 1; // 这就是显示赋值
{
num = 2; //代码块赋值
}
public A(){ // 构造器赋值
num = 3;
}
public static void main(String[] args){
A a = new A();
a.num = 4; // 有了对象后赋值
}
}
按照如下顺序
1、先加载这个属性
int num;
2、默认初始化
num = 0;
3、显式初始化 / 在代码块中初始化(这两个是同级 按照在类中出现的顺序执行)
// 由于显示赋值出现在代码块前
num = 1;
num = 2;
4、构造器中初始化
num = 3;
5、创建完对象后通过对象的set方法或者直接调属性赋值
num = 4;
静态的类似 。。
详细信息看下面
https://blog.csdn.net/a940902940902/article/details/56684669
属性的赋值方式及代码块调用时间 :JVM笔记
设计模式之单例设计模式
1.饿汉式(线程安全)提前造好对象
class A{
private static A instance = new A(); //私有的一个对象 在类加载的时候就创建好
private A(){ // 私有化构造器 在外部无法创建对象
}
public static A getInstance(){ // 静态方法 调用都返回同一个对象
retrun instance;
}
}
2.懒汉式(线程不安全)
class B{
private static A instance = null;
private A(){
}
public static A getInstance(){
if(instance == null){
instance = new A();
}
return instance;
}
}
7.final关键字
final可以修饰 类
方法
属性
- final 修饰类 该类不允许被继承 (String System StringBuffer StringBuilder等)
- final修饰 方法 该方法不允许被子类重写 但是仍然可以被子类继承使用 (Object类中的getClass方法 String的intern方法)
- final修饰属性 属性名必须大写 并且只能赋一次值 类中的属性要求在对象造好的时候final修饰的属性必须被初始化(非默认初始化)
- final修饰的引用类型的变量 在指向一个对象后 不允许在指向另一个对象
- final可以修饰方法中的形参
- final修饰的变量,在编译期就确定了值
1) 成员变量
1. 修饰类中的基本数据类型 必须在后面赋值 且值不可改变 即final修饰的变量只能赋一次值
final int A = 1; // 1.必须赋值 2.这里不赋值 在代码块中赋值 效果是一样的 最后都收集到了 init方法中
/*
final int A;
{
A = 2; // 第二种方式
}
*/
对象.A = 2 //错误 一旦创建对象后 这个A就不允许在被赋值
2.修饰类中的引用类型变量 必须在后面指向对象
final Student stu = new Student(); //跟上面一样 两种赋值方式 这是第一种
/*
final Object obj;
{
obj = new Object(); //第二种方式
}
*/
stu = new Student(); // 错误一旦创建对象后 stu这个引用不允许在指向其他对象
2) 局部变量在使用final定义的时候 可以先不为其赋值 但是在下面只能赋第一次值
void test(){
final int A; //正确 局部变量声明为final的时候可以先不赋值
A = 1; //正确
A = 2; //错误 只能一次值
// 引用类型
final Object obj; //正确 局部引用类型变量声明为final的时候可以先不赋值
obj = new Object(); //正确 obj指向了一个对象
obj = new Object(); // 错误 final声明的引用 只能指向一个对象 上行代码已经指定了对象引用
}
final修饰属性 可以显示初始化 代码块中初始化 构造器中初始化 (任选其一且只能选一个)
- 如果要求一个类所有的对象的这个值都一样 那么就使用
显示初始化
或者代码块赋值
- 如果final的是一个对象 初始化这个对象要去调方法可能抛异常等 就可以使用
代码块初始化
- 如果要求一个类的所有对象的这个值可以不同 就可以在
构造器中初始化
传入值进行初始化
总结上面 当final修饰属性时 要求当这个对象造出来的时候 这个属性一定是有值即被初始化过(非默认初始化
经常用static final 修饰一个属性 表示全局的常量 直接使用类名调用
8.abstract关键字
abstract关键字理解
1、只能修饰 类
方法
2、修饰类表示这是一个抽象类 (理解抽象的概念) 所以这个类不能实例化 只能等着子类去继承 其他和正常类都一样
3、修饰方法表示这是一个抽象方法 这个方法不能有==方法体== 即方法没有大括号{}
4、抽象类中可以写抽象方法显示的用abstract修饰方法(子类必须重写父类的所有抽象方法 如果没有重写全部抽象方法 那么子类也必须是一个抽象类 也得用abstract修饰 子父类的抽象方法也能继承) 也可以写非抽象方法(让子类继承时可以直接使用) (即抽象类中不一定有抽象方法)
5、非抽象类不能有抽象方法 (即非抽象类一定没有抽象方法)
6、abstract不能修饰 私有方法 静态方法 final修饰的方法和类(编译期直接报错)
7、抽象类也有多态性 可以使用匿名内部类的方式创建子类对象
abstract class A{
int a; //正确 可以定义属性
static int num; //正确 可以定义静态属性
public A(){ //正确 可以定义构造器
}
public A(String num){ 正确
}
public abstract void say(); //正确 可以定义抽象方法 但是不能有方法体
public void test(){ // 正确 可以定义非抽象方法
}
public static void test1(){ // 正确 可以定义静态方法
}
public final void sss(){ //正确 可以定义final的方法
}
public abstract final void test2();//错误 abstract和final一定不能同时出现 冲突
}
class B extends A{
int a;
//重写所有抽象方法 如果没有重写 所有的 抽象方法 那么类B也必须是一个抽象类
@Override
public void say(){
}
public abstract void tesst(){ //错误 非抽象类不能有抽象方法
}
}
//总结:如果一个类中只要有抽象方法 那么这个类一定是一个抽象类 但是抽象类中可以没有抽象方法
// 非抽象类中一定没有抽象方法
可以这样理解抽象类:抽象类就是比普通类多定义了抽象方法(可以没有抽象方法)除了不能实例化之外 并没有任何的不同
创建一个类的匿名子类的匿名对象(使用多态表示)
1、final类没有子类
2、 abstract修饰的类也可以
class A{
public static void main(String[] args){
A a = new A(){ // 小括号后加大括号
//可以重写方法
// 自定义的属性和方法在外部无法使用 因为根据多态 想要调子类独有的方法和属性
// 要求向下转型(强转为子类) 需要子类名 这里是匿名的子类 所以无法调用匿名类 //独有的属性和方法
}; // 这就创建了类A的匿名类的对象 使用多态表示了
}
}
设计模式之模板设计模式
抽象类体现的就是一种模板设计模式的设计 抽象类作为多个子类的通用模板,子类在抽象类的基础上进行拓展,改造,但子类总会保留抽象类的行为方式
解决的问题:
当方法内部的一部分功能实现是确定的,一部分实现是不确定的,这时就可以把不确定的部分暴露出去,让子类去实现
在实际的应用中非常广泛各个框架类库中都有其印制 如Servlet 我们在使用的时候只需要重写doGet/doPost方法即可实现不同的功能 还有在某些框架中 我们只需要重写指定的方法就可完成整个功能
abstract class A{
public void openDoor(){
System.out.println("开冰箱门");
}
public abstract void putDx();//这部分是不确定的 就可以定义为抽象方法 让子类去重写
public void closeDoor(){
System.out.println("关冰箱门");
}
// 某个功能 但是一部分是已实现的 一部分是不确定的 这一部分就让子类去重写
public final finPutDx(){
openDoor();
putDx();
closeDoor()
}
public static void main(String[] args){
SbuA subA = new SubA();
subA.finPutDx(); // 调用方法 其中的不确定的部分已经重写
}
}
class SubA extends A{
// 子类重写了不确定的方法 在调用整个方法的时候就是自己独有的
@Override
public void putDx(){
System.out.println("打晕Dx");
}
}
总结: 从这我们更加的理解了继承的另一种好处 就是拓展原类 就是一个类A有的方法属性 我们想要对类A进行扩充功能 属性等,我们就可以通过一个类去继承A 这样保证了类A不被破坏 且保证了继承的类可以拥有和A一样的功能并且可以对A进行拓展
9.数组及引用类型的数组
1.不管是什么类型的数组 都是属于引用类型的数据 即数组对象在堆中
2.数组的长度是不可变的
2.数组的创建方式有两种
- 先声明 后赋值
- 声明时直接赋值
两种方式创建数组的不同
class Student{
String name;
int age;
}
// 使用第一种方式 先声明 后赋值
// 1. 普通类型
int[] nums = new int[5]; // 这里一定要显示长度 因为这一步会在堆空间中开辟一段固定长度的空间 不写就报错
// 这时 在堆空间中已经有了一个存放数组的空间 长度为5 因为我们没有赋值 所以JVM会默认初始化数据 这里是int型 所有默认初始化的数据都是0
//初始化各个索引位置的数据
nums[0] = 1;
...
//2.引用类型
Student[] stu = new Student[3]; // 开辟内存空间 默认初始化全都是 null
stu[0] = new Student(); //初始化
stu[1] = new Student();
System.out.println(stu[2].name); // 空指针异常 stu[2] 是null 未赋值
// 使用第二种方式 声明时赋值
int[] nums = new int[]{1,2,3,4,5}; // 声明时直接赋值 不用写长度 JVM自动判断你的数据长度
Student[] stus = new Student[]{new Student();new Student();...};
String[] strs = new String[]{"asd","sd","sds"};
10.类的加载过程
11.对象的创建过程
第②模块.封装
封装性的体现
1.我们将类的属性私有化,同时提供公共的set和get方法
2.可以在类内部私有一个方法 在外边无法调用 供内部的方法使用 即不对外暴露私有的方法
3.单例模式 私有了构造器
。。。
四种修饰权限
1.private 只能在类内部使用
2.缺省(不写) 只能在一个包 或者类内部使用
3.protected 可以在子类中使用(开发中我们使用框架时 我们写的类不可能跟框架中的某个类在一个包中 所以我们只能使用子类的形式去使用protected修饰的类中的结构)
4.public 同一个工程的任何地方
权限修饰符可以修饰 类 方法 属性 构造器 内部类 代码块不能使用权限修饰符(只能使用static修饰或者默认)但是类只能使用缺省或者public修饰 方法 属性 构造器 内部类可以使用四种修饰符
权限表
修饰符 | 类内部 | 同一个包 | 不同包的子类 | 同一个工程 |
---|---|---|---|---|
private | Y | |||
缺省 | Y | Y | ||
protected | Y | Y | Y | |
public | Y | Y | Y | Y |
1、修饰类(非内部类) 只能使用 缺省 和 public
class A{ // 正确
}
public class B{ // 正确
}
private class C{ //错误 private不能修饰类
}
protected class D{ //错误 protected不能修饰类
}
总结封装性:Java提供了四种权限修饰符来修饰类及类的内部结构,体现类及类的内部结构在被调用时的可见性的大小
权限修饰符的深入理解
只看类与类的关系 跟对象没有任何关系 在每个对象中不管类的结构是什么权限修饰的 我每个对象都有 只是说类的位置(关系)的不同 我这些对象的内部的结构是否可用 因为类我们一般都定义为public 也就是说我对象你随便在哪造都行 但是对象中的内部结构你想用一个类中使用 那就得看这个类的与本类的关系
1.private修饰的结构 意思就是 A类中的私有属性 只能在A类内部出现且使用
2.缺省的修饰的 意思就是对象的结构只能在A类内部 或者是在A同包下的类中使用
3.protected受保护的
package a;
public class A{
String name; -- 缺省权限 只能在本类或者一个包下使用
protected int age; -- 受保护权限 本类 同包 子类中使用
}
package a;
public class B{
public void methodB(){
A a = new A();
a.name = "ss"; 可以调 在同一个包下
a.age = 1; 可以调 在同一个包下
}
}
package b;
public class C{
public void methodC(){
A a = new A(); //这个随便造 因为A类public的 所以构造器 也是public的
a.name = "xx"; --> 报错 因为这时 类C和类A不在一个包下 所以无法使用缺省修饰的
a.age = 1; -->报错 age属性是protected的 C类与A类不在一个包下 且C类不是A的子类
-->所以无法使用age属性
}
}
package c;
public class D extends A{
public void methodC(){
A a = new A();
a.age = 1; 正确 D是A的子类 可以在D类中使用age属性
}
}
private和public修饰的就不在演示 因为private修饰的只能在本类中使用,其他不管是在哪都不能用,而public是在哪都能使用
总结:我们上面演示的是在哪里都可以创建对象,这个可以通过构造器的修饰符来设置,构造器私有的 也就是说在类外部不能通过new的方式创建对象,
总之 类中的权限修饰的结构 是否能调 就在类与本类的关系 是否是一个包 是否是子类 等等
与对象没关系
第③模块.继承
继承的好处:
1.减少代码冗余
2.便于功能的扩展
3.为之后的多态提供了前提
Java支持单继承和多层继承 不支持多重继承
Java直接继承的父类称为直接父类,父类继承的类称为间接父类,子类继承父类以后就获取了直接父类以及间接父类的中声明的所有的属性与方法继承的隔离性理解
- 只要子类实现了继承 那么父类的所有的属性方法给你加载到你子类对象中去,就相当是从内存空间的角度来说 在子类对象中 子类独有的属性方法与父类的属性方法是完全隔离的,没有一点关系,只是说我子类可以通过super来调父类中的属性和方法 所以说 在父类中定义一个属性name是String的,我子类当然也可以定义一个String类型的name,当然也可以定义一个int 类型的name,跟父类中的name没有一点关系,当然也就不会冲突,我们在子类中想要用父类的name时 就用super来调,想用自己的name就用this来调,如果不加修饰 默认都是this,
如果说我们在子类中没有定义跟父类同名的属性 那么我们用this跟super都是一样的 去掉父类中的属性,用了this先去本类找是不是自己定义了,没找到就去父类的,用super直接就去父类中找 就直接调父类的 - 但是方法不一样 如果在父类中定义了一个方法 那么子类中只能定义跟父类中的方法重载的方法或者重写父类的方法,不允许定义同名但不是重载的方法
当子类中没有定义跟父类同名的属性时,在子类中 使用 this.属性 super.属性 调的都是父类的
当子类中定义了跟父类同名的属性(类型可以不同)时 这时就要使用 super 和 this 进行区分
因为在子类中调方法或者属性时默认都是this来修饰
class Dad{
String id;
void test(){}
}
class Son extends Dad{
int id; //类型不同不报错 因为只要定义了同名的属性 不管类型是啥,这时在子类中就是独有的属性, 但是父类的String类型的id在子类中想调就要使用 super.id调
@Override
void test(){} //正确 重写
void test(String name){} //正确 跟父类中的test方法形成了重载
int test(int age){} //正确 跟父类中的方法构成了重载
int test(){} //错误 跟父类中的方法不构成重载 也不是重写 就是跟父类中的方法冲突
}
子类实例化的全过程
Bilibili _P276
继承的重要理解
-
A 继承 B 那么A就获得了B(只获得)的所有(包括private和static的)
属性
和方法
(不会获得父类的构造器 代码块) 只不过是父类中声明了private的属性或者方法 子类中无法使用 实际上在子类对象的内存空间里 就有一块空间是存放父类所有的属性和方法 我们就可以理解为我们获得了父类的所有属性和方法 只是当父类的属性方法声明为private时我们就无法使用 -
子类如果没有定义与亚父类对象同名的属性,那么在内存的角度我们使用的就是父类亚对象的属性 这时的this和super没有区别 都指向了亚父类对象中的属性或方法
-
如果我们在子类中定义了跟父类同名的属性或者方法(方法好理解 就是重写)那么我们使用的就是真正意义上(从内存空间的角度)子类自己的属性或方法 这时的super和this就起了区分作用 super就是亚父类对象的 this就是子类的
-
我们在继承时 A类继承B类 B类有独有的属性name(非private的)我们在A子类的构造器里发现没有这个name属性 验证了我们说的 从内存角度说明了亚父类对象的存在
1.this关键字
表示当前对象或者表示当前正在创建的对象 可以修饰方法 属性 构造器
//在类的方法中 我们可以使用this.属性 或者this.方法的方式 调用当前对象的属性或方法 通常情况下我们都选择省略this 特殊情况下就不能省 如果方法的形参和类的属性同名 那么我们必须显示的使用this进行区分属性和形参 如下面的set方法
class A{
private String name;
public void setName(String name){
this.name = name // 这里必须使用this.name 不用的话 name = name 默认两个name都是形参 无法为属性赋值 使用this进行区分 是当前对象的属性name
}
public A(String name){
this.name = name //这里也必须加this 表示正在创建的对象
构造器里可以调方法
this.method1(); //表示当前正在创建的对象的方法
}
public void method1(){
this.method2(); //这里加不加this都可以 Java默认加了this
}
public void method2(){
}
}
this调用构造器
使用this()调用本类中的其他构造器 不能调用自己 各个构造器之间不能形成循环调用
规定 在构造器中调用其他的构造器时 必须放在首行(因为super()必须放在首行) 且一个构造器中 this() 和super() 不能同时出现 且只能调用一个其他的构造器
class Person{
private String name;
private int age;
public Person(){
this(); //报错 只能调用本类中的其他构造器 不能调用自己
}
public Person(String name){
this(); //调用空参的构造器 只执行代码 不是再次执行构造方法 且只能写在第一行(因为构造器里默认有super(); 它必须放在首行) 且一个构造器里只能调用一个本类的构造器 且只要调用了本类中的构造器就不允许在显示的调用父类的构造器 通俗来讲就是在一个构造器中 this();和super();不能同时使用
super(); // 错误 父类的构造器必须放在首行
}
public Person(int age){
System.out.println();
this(); //报错 只能写在第一行
}
}
2.super()关键字
super可以调 属性 方法 构造器
当父子类中的属性没有重名时
3.亚对象
https://blog.csdn.net/BigDevil_/article/details/103299064
1.创建子类对象时尽管调用了父类的构造器,但是不会创建父类对象 只是调用了父类的构造器初始化属性
2.this
指向了不仅父类中可继承的成员变量和可继承的方法外,它还指向了子类的成员变量和方法 而super
仅仅只是指向了子类对象中从父类继承的成员变量和方法。
3.在每个子类中都有一块空间是指向父类的==属性==与方法
结合 2 3
class Dad{
String name;
}
class Son{
String name; //如果这里我在子类中也定义name属性 那么构造器里的this和super就是不同的作用 this.name
//表示子类的name为其赋值为son super.name表示在子类的内存空间中的亚父类对象中的name属性 为其赋值为dad 所以我输出name就是son
//如果在子类中没有定义name属性 那么构造器里的this 和 super就是一个作用 都指向了在子类内存空间中的亚父类对象的属性name 所以我输出name 就是dad
public Son(){
this.name = "son";
super.name = "dad";
}
public static void main(String[] args){
Son son = new Son();
System.out.println(son.name);
}
}
总结:继承不是说在直接父类有啥属性和方法 我子类中也有 有是有 但是实际上还是父类的亚对象的
但是我们在日常使用的时候就不会考虑这些问题 默认的我们调用的就是子类继承父类的属性方法,
因为究其底 它终究还是子类对象 用的时候我们当成子类的属性就行
>4.方法的重写
规定
-
子类重写的方法的权限修饰符不能小于父类被重写的方法的权限修饰符 例如父类的方法是protected修饰的 子类重写的不能修饰为缺省或者private 只能使用protected或者public
-
子类无法使用也无法重写父类中private修饰的方法 我们就可以通俗的认为这个方法不存在 子类中可以定义同名但是不构成重载的方法
-
子类重写方法抛出的异常不能大于父类抛出的异常
https://blog.csdn.net/xichengfengyulou/article/details/93330694
-
关于返回值类型
父类的方法的返回值如果是void或者是基本数据类型 子类重写方法是返回值必须跟父类一样
返回值类型如果是引用类型A,那么子类重写方法的返回值可以是A及A的子类
-
父类中的静态方法没有重写这一说 final定义的方法子类不能重写
子类和父类的同名同参的方法要么都声明为非static的(父类中的方法只有是非static的,才有重写这一说),要么都声明为static的(非重写) 但是父类中加了final的静态方法 子类中不能有跟父类同名同参的静态方法 但是属性没有限制
class Dad{ static int a; static void methodStatic(){} static final methodStaticFinal(){} final void methodFinal(){} } class Person extends Dad{ int a; //正确 父子类中的同名属性没有限制 不写static不报错 void methodStatic(){} -->报错 跟父类同名同参 但是父类的这个方法是静态的 这里也声明为static static void methodStatic(){} //正确 但是这不是重写 static final methodStaticFinal(){} // 错误 一旦父类的静态方法定义成了final 那么子类无法在定义跟父类同名同参的方法了 @Override void methodFinal methodFinal(){} //报错 父类final的非静态方法 子类无法重写 } 对父类中的静态方法的总结 1.一般来说父类中的静态方法子类都不动 不去声明跟父类一样的静态方法 也没有意义 我们可以通过子类的类名.方法 去调父类中的静态方法 除非特殊情况我们要'重写'父类中的静态方法 我们就去定义跟父类同参同名的静态方法, 在方法体内写自己的代码即可
三种方式
1.继承一个父类 可以重写父类的方法
2.实现一个接口 重写接口中的抽象方法
3.继承一个抽象类 重写抽象方法
抽象类中可以有非抽象方法 非抽象类不能有抽象方法
5.Object类及方法
1、Object类没有属性
2、只有一个空参的构造器
3、主要方法
-
clone 克隆 深拷贝与浅拷贝
protected Object clone() //TODO
-
finalize(对象回收之前会调此方法)
protected void finalize()
-
equals
1、equals() 和 == 的区别
1) == 可以使用在基本数据类型和引用数据类型
① 比较基本数据类型的时候 比较两个变量的值是否相等 (类型不一定要相同int 可以和 char做 ==)
注意:所有基本数据类型的数组类型也是引用类型 跟普通的引用类型没有任何区别
② 比较引用类型数据的时候 比较的是两个对象的地址值是否相同
- equals只能使用在引用数据类型 因为他是对象中的方法 只能通过对象来调
① 如果类没有重写过Object类的equals方法 那么使用equals比较的就是对象的地址值 因为 - Object类的equals的源码就是使用的 ==
② 如果类重写了Object类的equals方法 如果使用equals那就不一定了
如String Date File 包装类等 都重写了equals方法 它们比较的就是内容是否相等
因为String存在着一个常量池的缘故,所以我么在开发中一定要注意,想要比较两个字符串的内 容是否相等时,一定要用
equals()
, 有可能两个内容相同的字符串==是false总结 :大部分情况下 equals 和 == 没有区别 都是比较对象的地址值
特殊的 只需牢记 String Date File 包装类等 重写了eauals方法的类 比较是内容
boolean equals(Object obj) //返回值是一个布尔值 传入一个对象 // 自定义类重写equals方法 比较a对象的name属性是否相等 class A{ String name; @Override public boolean equals(Object obj) { xxx... }
-
getClass 获取对象的所属类(Class)对象
Class getClass() //返回值是一个Class对象,空参 // 使用举例 使用类的对象来调用 class A{ public static void main(String[] args){ A a = new A(); Class clazz = a.getClass(); } }
-
toString()
当我们输出对象的引用时,实际上调用的就是对象的toString方法 所有对象的toString方法只要没有 重写过 返回的都是地址值 除了 Stirng Date File 包装类等重写了toString方法 (返回)输出的是内容
但是char型数组 直接输出引用的时候会输出数组 这是println方法的缘故 输出char型数组对象的toString方法还是地址 这个
数组类型是引用型,但是由于数组类型无法重写toString方法 所以数组类型的toString方法只能是地址值
所以只要牢记只要没有重写过 toStirng方法 输出的toString方法就是地址
String toString(); //返回值是一个String 空参 //Object类中的toString方法的定义 public String toString() { 返回的就是 当前对象的类名+@+对象在堆空间的地址值经过hashCode算出来的 return getClass().getName() + "@" + Integer.toHexString(hashCode()); } // class A{ String name; public static void main(String[] args){ A a = new A(); System.out.println(a); } }
-
wait
-
hashCode
-
notify
-
notifyAll
第④模块.多态
对象的多态性 前提:1.是类之间必须有继承关系 2.子类重写了父类的方法 如果没有重写 那调用的还是父类的方法 就没有意义了
接口的多态性:前提是类必须实现接口:
1、静态绑定与动态绑定
https://blog.csdn.net/BigDevil_/article/details/103295593
总结:
- 所有
属性(包括静态)
和静态方法
都是静态绑定 只看左边的父类如果 如果不强转为子类对象,使用父类对象调属性和静态方法 就去父类中找 如果强转为子类对象了,那么就可以调子类的属性和静态方法了
class Dad{
String name = "dad";
static int age = "50";
static void method1(){
System.out.println("父类的静态方法");
}
}
class Son{
String name = "son"; //同名非静态属性
static int age = "21"; //同名静态属性
static void method1(){ // 同名静态方法
System.out.println("子类的静态方法");
}
public static void main(String[] args){
Dad dad = new Son();
// **********静态绑定****************
// 看左边 是Dad类的属性和静态方法 就去Dad类中找
System.out.println(dad.name); // 输出 "dad"
System.out.println(dad.age); // 输出 50
dad.method1(); //输出 "父类的静态方法"
//想要调子类独有的属性和静态方法 必须强转
//1. Person person = (Person)dad;
((Person)dad).name
((Person)dad).age
((Person)dad).method1();
}
}
-
对于
非静态方法
属于动态绑定,看右边子类的对象,使用dad.调方法时,看一下子类是否重写了父类的方法,如果重写了就调子类重写的方法,没有重写,就调子类继承父类的方法 ,如果想要调子类独有的方法 必须强转class Dad{ void method1(){ System.out.println("父类的方法"); } void method2(){ System.out.println("父类的方法2"); } } class Son{ @Override void method1(){ System.out.println("子类重写父类的方法"); } void method(){ System.out.println("子类独有的方法"); } public static void main(String[] args){ Dad dad = new Son(); dad.method1(); //输出 "子类重写父类的方法" 运行期去子类中找 dad.method2(); //跟method1一样 去子类找 // dad.method3(); //报错dad中没有method2方法 这是子类独有的 想调必须强转 ((Person)dad).method3(); //正确 强转调子类独有的方法 } }
instanceof关键字
https://blog.csdn.net/kuangay/article/details/81563992
对象 instanceof 类
- 例如 a instanceof A 判断a 是否是A类的对象(a指向A的一个对象) 返回true表示是 false表示不是
- 使用情景 为了避免在向下转型时出现 ClassCastException异常 我们在向下转型前,先进行instanceof判断,一旦返回true就向下转型,如果返回false 不进行转型
- a instance A 如果是true 那么a instance A的父类也是true a instanceof Object 一定是true
2、内部类总结
如果一个类中只有一个私有的构造器 那么这个类没有子类 因为子类的构造器默认调用父类的构造器 父类的构造器如果是私有的那么调用不了 也就是说无法创建子类对象 也就没有子类
关于 private 修饰构造器 和 protected修饰构造器的区别
// private修饰一个类的构造器 那么这个类没有子类
class Dad{
private Dad(){ //如果只有一个私有的无参构造器 那么这个类没有子类
}
}
class Person{
Person(){
super(); // 因为子类的构造器默认在第一行调用父类的无参构造器中 如果父类的构造器是私有的 就调不来了,也就无法创建子类对象 也即无法创建匿名内部类
}
}
//***************** protected修饰构造器 可以有子类继承
class Dad{
protected Dad(){ //如果只有一个受保护的无参构造器 那么这个类可以有子类
}
}
class Person{
Person(){
super(); //受保护的即只能在本类 一个包 或者子类中使用 那么这里调父类的构造器就可以调 即可以创建子类的对象 也就是protected修饰的构造器可以为其创建匿名内部类
}
3、接口interface
1、Java中接口和类是并列关系
2、一个类只能继承一个类 但是可以实现多个接口(变相的多重继承)
3、接口与接口之间可以多重继承 即一个接口可以extends多个接口
4、在接口中可以定义什么
-
JDK7及以前:只能定义
全局常量
和抽象方法
①全局常量:public static final 修饰
②抽象方法:public abstract 修饰
可以省略 但是不能使用private protected等显示的修饰 直接报错
-
JDK8: 除了定义全局常量和抽象方法之外,还可以定义静态方法 默认方法
JDK8接口的新特性
1、接口中定义的静态方法 必须有方法体 但是实现类完全是用不了 只能通过接口.静态方法来调用即接口中的静态方法只能接口自己使用 实现类用不了
interface JDK8newInter{
static void test(){ // 接口中的静态方法 必须有方法体
System.out.println("Hello World");
}
}
class A implements JDK8newInter{
public static void main(String[] args){
A a = new A();
a.test(); // 报错 method is undefined (方法未定义) 无法使用接口中的静态方法
JDK8newInter.test(); //正确 只能通过接口来调
}
}
2、接口中定义的默认方法 可以使用实现类的对象调用 也可以重写 如果一个类实现了多个接口,多个接口中都有默认的方法且都同名同参,这就是接口冲突
这时此类必须强制重写同名的默认方法 ,但是如果这时此类又继承了A类,且A类中也有跟接口中的方法同名同参的方法,那么这时就不必强制重写,默认调父类中的那个同名方法 即父类优先
一般的我们都不会重写接口中的默认方法,没有什么意义,实现类就直接使用即可
康师傅P_357
public interface Inter1 {
default void method1(String name){
System.out.println("Inter1的方法");
}
}
interface Inter2 {
default void method1(){
System.out.println("Inter1的方法");
}
}
class SuperCls {
public void method1(){
System.out.println("Inter1的方法");
}
}
class A implements Inter1,Inter2 {
@Override //这里不重写同名方法 直接报错
public void method1(){
}
}
class B extends SuperCls implements Inter1,Inter2{
// 不报错 默认父类优先 调用父类的同名方法
@Override //也可以重写
public void method1(){
}
public void test(){
method1(); // 调的是自己重写的
super.method1(); //调的是父类的
Inter1.super.method1(); //调的是接口Inter1中的默认方法 只能在实现类中这样调
Inter2.super.method1(); //调的是接口Inter2中的默认方法 只能在实现类中这样调 // 使用 接口.super.方法 (只能在实现类中这样使用)
}
}
5、接口中不能定义构造器 不能实例化
6、如果一个类实现了一个接口并重写了接口中的所有抽象方法,那么这个类可以实例化(即非抽象类),如果没有重写全部抽象方法,那么这个实现类必须要用abstract标示(即该实现类是一个抽象类) 跟抽象类的继承相似
7、接口的具体使用 体现多态性
8、如果类A 继承了类C 类C实现了接口Inter1 接口Inter1继承了接口inter2和Inter3 那么 A C Inter1 inter2 Inter3就是一颗继承树上的 类C 及三个接口都能用多态表示A的对象
因为A继承了类C 类C可以多态A 而C实现了Inter1接口 变向的A也实现了Inter1接口 所以可以多态 而Inter1又继承了Inter2 Inter3接口 变向的A也实现了Inter2 Inter3接口 所以可以多态
>、接口的匿名实现类的对象
interface Inter1{
void test();
}
class Test{
public static void main(String[] args){
//下面就创建了接口的匿名实现类 使用接口类型多态的形式表示
Inter inter = new Inter(){ //小括号后跟大括号
@Override
public void test(){
xxx...
}
}
}
}
4、关于匿名内部类 接口与类的不同
匿名内部好处
可以使命名变得简洁
使代码更加紧凑,简洁,封装性比内部类更优
一个类用于继承其他类或是实现接口,无需增加其他的方法,只是对继承方法实现 覆盖。
1.类
class A{
void mehtodA(){};
}
class Test{
void method1(){
A a = new A(){ // 类从这行开始
//在这个大括号之间就是一个A的子类 一个类能写的这里面就能写(不能写静态属性方法代码块) 但是是 没意义的 在外面无法调用
// 根据多态的静态和动态绑定 如果我们想要使用子类独有的属性或方法 那么我们必须强转为子类
//也就必须使用到子类的类名 匿名的也就没有子类的类名 也就无法调用在这里面写的
int a;
void test(){
}
@Override //可以重写 抽象的方法必须重写
void methodA(){
}
....
}; // 从这行结束
//使用多态进行调用
ca就是父类的引用 但是这里跟多态不同的是 只能调父类的方法 不能调子类独有的方法
因为子类没有类名 无法强转为子类 也就调不了子类的独有属性方法
}
}
2.接口
interface Ia{
void methodIa(){};
}
class Test{
Ia ia = new Ia(){
//这里必须重写methodIa 因为根据接口的定义 接口的实现类 必须在定义类的时候就必须接口中的抽象方法
// 这点是跟类的匿名内部类不同的地方
@Override
void methodIa(){
。。。
}
}
5、设计模式之代理模式(Spring笔记)
- 代理模式分为:
1、静态代理
2、动态代理(动态生成跟原始类实现同一个接口的代理类)
想一下 通过动态字节码文件如何生成一个跟原始类既实现同一个接口 又有相同的方法且接入了额外功能 即Proxy.newProxyInstance()返回代理对象的参数都应该传入什么参数
第一点:
想要通过动态字节码技术生成类并创建大的Class对象 肯定需要类加载器 即ClassLoader第二点:
想要跟原始类实现同一个接口 那就传入原始类实现的接口(可以不止一个)(传入实现的所有接口)第三点:
想要有原始类重写接口的方法 并加入我们的额外功能 就传我们写好的InvocationHandler对象 里面定义了原始方法的调用和我们加入的额外功能 (实现InvocationHandler接口去定义额外功能
)
通过这三个参数 我们就具备了可以生成符合条件的代理类的对象 CGLib代理同理 就把第二个参数换原始类的父类即可 因为我们要生成原始类的子类对象去代理 CgLib的额外功能是去实现MethodInterceptor
3、CGLib动态代理(动态生成原始类的子类)
-
代理模式的本质 有原始类 通过另一个类实现跟原始类同一个接口或者是原始类的子类 为原始类中的方法增加额外功能
一句话:代理模式就是为原始类的方法增加额外功能
所以代理模式的开发步骤
==1、==原始类(对象)
==2、==原始方法(切入点)
==3、==额外功能
4、(可动态生成)代理类(与原始类实现同一个接口或者是原始类的子类)
==5、==调用代理类的方法 里面有原始类的方法和额外功能 就完成了代理的功能
设计模式之工厂模式(Spring笔记)
1.简单工厂模式
2.工厂方法模式
3.抽象工厂模式
4.Spring的通用工厂模式
第⑤模块.包装类
1.包装类具有类的基本特征 包括属性 方法 构造器等
2.所有的包装类都重写了Object类中的toString方法和equals方法 toString返回String类型的内容(不再是地址) equals比较的也是内容(不再是地址)
3.前面说过 类成员如果是基本数据类型的话 在创建对象时有默认的初始化功能,0 false的等等,但是如果成员变量是包装类的话 初始化全部都是都是null
基本数据类型 | 包装类 | 直接父类 |
---|---|---|
byte | Byte | Number |
short | Short | Number |
int | Integer | Number |
long | Long | Number |
float | Float | Number |
double | Double | Number |
boolean | Boolean | 无 |
char | Character | 无 |
1、基本数据类型与包装类的转换
基本数据类型转包装类-调用包装类的构造器即可
//基本数据类型转包装类 调用包装类的构造器传值即可 或者直接使用包装类声明变量
@Test
public void test(){
int num = 1;
Integer wnum = new Integer(num);
Integer wnum2 = new Integer(2);
Integer wnum3 = new Integer("3"); //这里注意字符串里面一定要是数字 否则转换异常
// byte float double short char 跟Integer类似
// Boolean有些不同
boolean b = false;
Boolean b1 = new Boolean("asad"); //这里跟上面的不同 这里可以传不是true false 的 因为在内部优化了 只要不是true(大小写都识别) 其余不管是啥都是false
}
包装类转基本数据类型 调用包装类的 xxxValue方法
@Test
public void test(){
Integer num = new Integer(1);
int inum = num.intValue(); //调用包装类的intValue() 方法返回基本数据类型的值
//其他的几种一样
}
自动装箱与拆箱
自动装箱(基本数据类型变包装类) 直接把基本数据类型赋给对应的包装类 (JDK5新特性)
Integer num = 1;
Boolean b = false;
...
自动拆箱(包装类变基本数据类型) 直接把包装类赋给对应的基本数据类型
Integer num = new Integer(1);
int inum = num;
-- 应用
public void method1(Object obj){} 方法参数是 Object 如果没有自动装箱 传基本类型的数据无法传 但是现在可以传基本类型的数据 就是因为自动装箱 自动的把基本类型装为包装类
基本数据类型包装类转为String类型
基本数据类型/包装类转为 String
两种方法 Stirng.valueOf 或者 包装类的toString方法(重写过)
@Test
public void test(){
int inum = 1;
Integer num = 1;
String s = String.valueOf(num);
String s1 = String.valueOf(inum);
String s2 = num.toString(); //包装类重写了toStirng方法 返回的就是String类型的内容
}
Stirng转换为包装类/基本数据类型
@Test
public void test2(){
String str = "123";
int a = Integer.parseInt(str);//返回值是基本数据类型的 但是由于自动装箱 也可以 用Integer来接收
Integer a = Integer.parsInt(str); 注意字符串内容要与转换成的基本数据类型一致
}
关于包装类Integer等的内部缓存
Integer的内部有一个 从 -128到127的一个缓存池 类似的其他的包装类都有
@Test
public void test(){
Integer a1 = new Integer(1);
Integer a2 = new Integer(1);
System.out.println(a1 == a2); //false new出来的 本身就不是一个对象
Integer a3 = 1;
Integer a4 = 1;
System.out.println(a3 == a4); // true 在-127到128范围内 直接从内部缓存中取
Integer a5 = 128;
Integer a6 = 128;
System.out.println(a5 == a6); //false 超出了范围 相当于new
}
第⑥模块.内部类
1.总体分为
-
①成员内部类(静态 非静态)
可以定义什么?
一方面可以作为外部类的成员
->可以调用外部类的属性 方法(遵守static和权限)普通内部类内部无法定义static修饰的方法属性
->可以使用static修饰类(静态内部类)
->可以使用四种不同的权限修饰
另一方面作为一个类
> 可以定义属性 方法 构造器 不使用final就可以被继承 使用final修饰 不能被继承
> 可以使用abstract修饰
②局部内部类(方法中 代码块中 构造器里)
关注的问题
1、如何实例化成员内部类的对象
public class InnerClassTest {
//创建静态内部类的对象
// 直接使用外部类.内部类的方式创建 因为是 静态 内部类
OuterClass.StaticInnerClassA innerClaA = new OuterClass.StaticInnerClassA();
//创建普通内部类的对象
//1、先创建外部类对象
OuterClass oc = new OuterClass();
//2、在创建内部类对象 左边使用 外部类.内部类声明类型
OuterClass.InnerClassB innerClassB = oc.new InnerClassB();
}
//外部类
class OuterClass {
//静态内部类
static class StaticInnerClassA {
}
//普通的内部类
class InnerClassB {
}
}
2、如何在成员内部类中区分调用外部类的结构
public class InnerClassTest {
//创建静态内部类的对象
OuterClass.StaticInnerClassA innerClaA = new OuterClass.StaticInnerClassA();
//创建普通内部类的对象
OuterClass oc = new OuterClass();
OuterClass.InnerClassB innerClassB = oc.new InnerClassB();
}
class OuterClass {
String name;
//静态内部类
static class StaticInnerClassA {
}
//普通的内部类
class InnerClassB {
static int age; //报错 非静态内部类中不能定义静态结构
String name;
public void test(String name){
System.out.println(name); //形参的
System.out.println(this.name); //本类的
System.out.println(OuterClass.this.name); //调用外部类的 必须使用这种方法
}
}
}
3、开发中局部内部类的使用
一般在方法中使用 局部匿名内部类
public class NmNbl {
public void test(Comparator comparator){
}
//1、使用标准的有名的内部类
public Comparator getCompartor(){
class SubCom implements Comparator {
@Override
public int compare(Object o1, Object o2) {
return 0;
}
}
return new SubCom();
}
//2、直接使用匿名内部类的方式
public Comparator getCompartor1(){
return new Comparator() {
@Override
public int compare(Object o1, Object o2) {
return 0;
}
};
}
public static void main(String[] args){
NmNbl nmNbl = new NmNbl();
///3、传参时使用匿名内部类
nmNbl.test(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
return 0;
}
});
}
}
4、关于局部变量与局部内部类的问题
public class TestA {
public void test() {
int a = 1;
Thread thread = new Thread() { //局部的匿名类对象 不能在内部类方法内部给外 部属性赋值
int c = a; // 可以赋给c 只要内部了出现了外部的局部变量 那么这个局部变量在外部就不允许赋值
@Override
public void run() {
//a += 1; 报错 在局部类内部不能修改外部的值
}
};
a = 3;// 只要内部类中用了外部的属性 那么在外部就不允许在使用
即 这两个地方只能二选一使用 因为本质上如果内部类想要使用外部的局部变量的话,外部属性的必须声明为final 即外部就不能再次赋值 JDK7即之前必须显式的声明为final JDk8之后可以省略,如果在内部类中使用了那么外部的局部变量就自动加final
}
}
while循环与for循环
循环结构的四个要素
- ①初始条件
- ②循环条件 只要循环满足循环条件 则将一直循环下去
- ③循环体
- ④迭代条件
---------------------------while-----------------------
void test(){
int i = 1; 1初始条件
while(i<2){ 2循环条件 布尔表达式
System.out.print("sss"); 3循环体
i++; 4迭代条件
}
执行顺序 1 2 3 4 2 3 4 2 3 4 。。。只要满足循环条件将一直下去 如果没有迭代条件那就进入了死循环 无终止的循环
--------------------do-while-------------------------
int i =1; 1初始条件
do{
3 循环体
4 迭代条件
}while(2循环条件);
1 3 4 2 2 3 4 2 3 4 2 3 4
// 先执行一遍循环体在循环 至少执行一遍循环体
----------------------for--------------------------
1初始条件 2循环条件 4迭代条件
for(int i = 1; i<2; i++){
3循环体
System.out.print("sss");
}
1 2 3 4 2 3 4 2 3 4 、、、、
}
而if和while的区别在于 if只是个判断条件 while是循环结构
只要满足while条件的将进入循环体一直运行,不满足时才会跳出循环结构,而if只要满足了条件就进入,执行完后就跳出了if语句,不满足直接不执行if语句。