下述内容为我个人阶段学习知识总结,以问答的形式呈现,均个人理解难免有误,如有错误请指教,转载请注明!
一、Java中为什么采用面向对象的编程方式?
之所以采用这种方式正是因为我们人习惯以对象的方式认识世界
,怎么说?假如我说熊猫,你的脑海中绝对会映射出大熊猫的外形特征和行为特点……
二、Java编程中若想创建一个对象必须要先有类的存在,那么对象和类的关系是什么呢?
- "
对象是类的实例,类是对象的抽象
" ,对象是类中的某个个体,类是对象共同特征的抽象模板。 - 对象是现实世界中
真实存在的事物
,而类则是抽象的概念
。例如:明星是一个类,胡歌、刘德华、周星驰这些就是明星这个类中的对象。明星这个类中状态(属性):姓名、年龄、性别……;行为(方法):演戏、唱、跳……。
三、对象和类怎么创建?
- 物理世界中 " 类 = 状态 + 行为 ",而在虚拟世界中状态就是属性,行为就是方法,因此 "
类 = 属性 + 方法
",类的创建需要关键字 "class
" ,有 public class CLASS_NAME{ } 、class CLASS_NAME{ }两种方式创建,一个文件中允许存在多个类,但只允许存在一个被 public 修饰的类
,一旦类被public修饰,那么这个类名必须和文件名一致。 - 对象的创建必须在拥有类之前,因为
类是一个模板,是一个参照
,好比建筑施工前必须先要有施工图纸。对象的创建通过关键字 "new
" ,CLASS_NAME VAR_NAME = new CLASS_NAME(……)
四、Java中属性以什么形式存在、为什么?方法呢?
- Java中属性以
成员变量
的形式存在,成员变量又分为实例变量和静态变量
;方法以静态方法和实例方法
的形式存在。 - 为什么属性以变量的形式存在呢?因为属性的本质是
数据
,年龄:20,身高:1.8m,颜值:99,变量就是用来存储数据的。 - – 实例变量:对象级别的变量,是
个体层面
的。需先有对象才能访问,每一个对象的属性不同。
– 实例方法:也是同样的概念,采用实例方法则表明方法体中调用了对象相关的数据
。例如:调用打篮球方法,某某人打篮球,小李打篮球、小王打篮球,不同对象之间; - 静态变量、静态方法:类层面的变量、类层面的方法,是
整体层面
的。
例如:中国人这个类中,国籍属性一定是中国,这个毋庸置疑,既然这样子那么每一个中国人的对象就一定都一样啦!我们只需要把它定义成静态的(static)即可,不需要在创建对象时在每个对象内部都开辟一个空间用来存放国籍信息
,几个对象倒还好,如果几百上千中国人的对象呢?内存开销将非常巨大
。静态方法也是一样的概念,只要这个方法是整体层面的且这个方法体内未访问对象相关的数据
,那么就能定义成静态方法。例如:学生都有学习这个行为,那么就能定义个学习方法,我不用某某人学习,我只要这个类的对象调用了学习方法那么就输出 " 学习 " ,这时候完全可以定义成静态方法。
五、Java内存模型中关键的几块区域以及区域中主要存储或执行哪些东西?简单介绍?
JVM规范中定义了Java内存模型,关键的有栈内存、堆内存、方法区
。
栈内存:方法调用时存储的位置、局部变量...
堆内存:通过 " new " 创建的对象、实例变量...
方法区:存储代码片段、静态变量、字符串...
- 当一个方法执行时,准确来说实例方法执行时(
压栈或入栈
),每个方法都会建立自己的内存栈,在这个方法体内定义的变量(局部变量
)将都会被放入这块栈内存里,随着方法中的最后一条语句结束,方法也就结束(弹栈或出栈
),这个方法的内存栈也将会自然销毁。所以,所有在方法体中定义的局部变量都是放在栈内存中的。
- 创建一个对象时,这个
对象实际上是在堆内存中被创建
,对象可以被反复使用。堆内存中的对象不会随着方法的结束而销毁,只要对象还有被引用,对象将一直存在下去,一旦对象失去了和任何 " 引用" 关联, 那么对象才会被 " 垃圾回收器 " 在适合的时机当作垃圾回收
。实例变量是属于对象的那么实例变量就存储在对象的内部
。 - JVM加载进来的字节码文件会保存在方法区,方法区保存着代码片段。类加载之后静态变量创建,
静态变量的访问不需要创建对象
,通过 "类名.
" 方式即可访问。
六、通过 " 引用. " 的方式调用实例变量或实例方法时候会出现 " 空指针异常的报错信息(java.lang.NullPointerException) " 的原因?静态变量和静态方法怎么没有?
空引用访问对象相关的数据
才会出现此异常。" 引用 " 指的就是变量,准确来说可以看作是C语言中的 " 指针变量 ",它保存着对象在堆内存中的内存地址
。空引用,指的是引用被赋予值 "null
" 或未赋值
,但只有满足 " 空引用 " 和 " 访问对象相关的数据 " 才会出现此异常。- 静态变量、静态方法是不需要创建对象通过类名就可以访问的,因此
静态变量和静态方法不存在空指针异常
。需要注意,**虽然通过 " 引用. " 也可以调用静态变量和静态方法,但与对象无关,编译器最终还是会通过 " 类名. " 的方式调用**,这句话超级重要。
七、什么是方法重载(Overload)?好处?什么时候采用方法重载?
- 方法重载:
同一个类中多个方法名相同、形式参数列表不同的方法
。 或者也可以说:同一类中具有不止一个实现相同功能的同名方法
例如:System.out.println()
就是一个方法重载 - 好处
便于编程、理解和记忆
。
试想下没有方法重载,C语言中没有方法重载,若想要创建具有求和的函数,则需要取不同的函数名多么的不便。 - 条件:
- 同属一个类
- 方法名相同
- 形式参数不同
:参数个数不同、参数类型不同、参数顺序不同
注意:方法重载只与方法名和形参列表有关。与修饰符列表、返回值类型、形参的变量名均无关
;不要和方法覆盖(方法重写)混淆了。
八、this是什么?this存在哪里?介绍?this()?
- this 是关键字、是引用,其保存对象自身内存地址。
this 存储在堆内存中对象的内部,保存着对象自身的地址。
- this 只能存在于实例方法中或构造方法中,
谁调用这个方法 this 就是谁
- this 大部分的情况下可以省略,但在Getter and Setter中不能省略。其实不然,this 并不是不能省略只不过是为了形式参数名要 "
见名知意
" 而做出的让步,如果不加 this 的话,根据变量的就近原则
就变成了局部变量赋值给局部变量。所以,this 在这里的作用是区别 局部变量 和 实例变量 。
- this() 只能存在于构造方法的
第一行
,表示通过本类的构造方法去调用本类中相应的构造方法,通过传入this()的参数
判别调用的其它构造方法。此方法常用于设置属性的默认值
。例如:
public class basketballTeam {
private String name; //姓名
private String likes; //喜好
//构造方法
public basketballTeam(String name) {
this(name,"打篮球"); //默认初始化属性
}
//构造方法
public basketballTeam(String name, String likes) {
this.name = name;
this.likes = likes;
}
......
......
}
九、什么是封装?为什么要封装?如何进行封装?
- 封装:
隐藏对象的属性和实现细节,仅对外公开接口
,控制程序对属性的 "读取
" 和 "修改
" 的访问权限;将属性和方法结合在一起,形成 "类
" 。 - 为什么要封装?对属性进行私有化目的是为了
保护内部结构安全、保证数据合理性
。
例如:我们定义一个 " 人类 ",类中有个属性:年龄,年龄一定有范围,但不论怎么说一定是>= 0
,如果不给年龄设置私有的访问权限
,那么它将默认使用 default(更高级别访问权限就不说了),在本包中的其他类将有权力对年龄属性随意修改
,难免有人赋一个-1
值,这样子就违背了……。而属性私有化之后仅允许属性在本类中进行修改,这就造成了外部无法访问的问题,故此,才有了 Getter and Setter。 - 如何进行封装?
第一:属性私有化;
第二:对外提供简单的操作入口(Getter and Setter、构造方法)
注意:get、set方法对外进行访问限制时,基本情况下构造方法也需要做相应的处理。
注意:方法也可以加 private ? 是的,方法加上 private 基本上表示仅类内部调用作为一种工具。
十、什么是继承?继承的条件?继承的作用?介绍?
-
什么是继承?指的是
子类
继承父类
非 private 修饰的属性及方法,构造方法也不能继承。子类又称派生类、扩展类,父类又称超类、基类。
注意:即使是被私有化的属性和方法也可以通过特殊的手段进行访问
。 -
继承的条件?
第一:必须满足从属关系
。例如:中国银行属于银行、圆桌属于桌子……
第二:类与类之间的冗余较高
。也就是既满足从属关系父子类之间又具有较多重复的代码,这时候就可以使用。 -
继承的作用:
基本作用:代码复用、降低代码臃肿
主要作用:因为有了继承关系,才有了方法覆盖和多态机制
-
Java不支持多继承,但支持多重继承,也就是间接继承,如:A继承于B,B继承于C,相当于A继承于C;Java中的类若没有继承任何类则默认继承Object类,不需要手动导包自动导入,归于Java.lang包;因为继承类与类之间耦合度非常高,父类一经变化,子类就受影响。
十一、什么情况下考虑方法覆盖Override或方法重写Overwirter,方法覆盖的条件?
-
什么情况下考虑方法覆盖?
当子类继承父类之后,继承过来的方法无法满足业务需求的时候
子类有权对此方法进行重写 -
方法覆盖的条件?
-继承
(不同类之间)
-参数列表相同
-方法名相同
-对于基本数据类型:返回值类型必须相同;对于引用数据类型:返回值类型必须相同或为其子类
注意:(一般原模原样复制粘贴进行覆盖,这个其实意义不大)
- 访问权限不能低于
父类
- 重写之后的方法不能比之前的方法抛出更多的异常
这里有几个注意事项:第一: 被 private、final 修饰的方法、构造方法无法被覆盖;第二:
方法覆盖只是针对于 " 实例方法 " 和属性无关," 静态方法覆盖 " 没有意义。
十二、多态?
- 多态:
同一行为具有多种不同表现形式
。编译期做静态绑定,运行期做动态绑定。
举个最近遇到的问题:在 IntelliJ IDEA 我使用快捷键 Ctrl + Alt + L 会使代码对其,而在QQ中按下同样的快捷键却锁定了QQ。相同操作作用于不同对象上呈现的效果不同
。 - 多态存在的三个必要条件:
继承
、方法覆盖(重写)
、父类型的引用指向子类型的对象
Animal dog = new Dog() - 这个人写的很不错,Java创建子类对象会不会创建父类对象&&静态绑定和动态绑定问题。
Animal dog = new Dog() 倘若dog 对象调用父子类共有实例方法
,在编译阶段,编译器判断出 dog 属于 Animal 类型,于是到Animal.class
下有相应的信息,找到了!编译通过静态绑定
。但是运行阶段
的时候和底层堆内存当中的实际对象有关,真正执行的时候会自动调用 " 堆内存中真实对象 " 的相关方法。
- 讲一下
向上转型(UpCasting)
和向下转型(DownCasting)
。
上面说的就是向上转型,只不过由于方法覆盖出现了特殊的情况。所以向上转型可以总结为父类引用指向子类对象,该引用无法调用子类特有的方法
。向下转型:父类引用指向子类对象,该引用调用子类独有的方法
。这个时候就要强制类型转换符参与了将 Animal dog 转成 Dog dog 使用(Dog)dog, Dog dog2 = (Dog)dog,这块其实和基本数据类型的转换很相似。多态消除了或说打破了类型之间的耦合关系,使得子类可以方便地添加属于自己方法,
显而易见这是因为多态中的向下转型的作用。 - 向下转型这里还会接触到
instanceof
这个关键字,用法就是 dog instanceof Dog ,表示 dog 是否是 Dog 的实例,是则返回true,不是则返回false。之所以有这个的存在是因为向下转型有风险
,ClassCastException(类型转换异常)
,看下述仅通过主函数的第二句话能推断猫一定是猫类的实例吗? 其实是不能的,cat 可能是 Animal 的任何子类,只不过编译阶段被强制转成了 Dog 静态绑定通过了,运行阶段才发现错误。
实际开发中,大家分工合作,指不定对方传入的参数是一只什么动物,不可能知道那么全面。
//动物
class Animal {
}
//猫
class Cat extends Animal{
}
//狗
class Dogs extends Animal{
}
class Main {
public static void main(String[] args) {
Animal cat = new Dogs();
Cat c = (Cat)cat;//编译成功运行报错
}
}