类名大驼峰,方法名/变量小驼峰
- 类只是一个模型一样的定西,对一个实体进行描述,限定了类中的成员
- 类是自定义的类型,可以用来定义变量
- 一个类可以实例化出多个对象,实例化出的对象,占用实际的物理空间,存储类成员变量
一. 封装
1.1 封装的概念
面向对象程序(OOP)三大特征:封装,继承和多态,在类和对象阶段,主要研究封装,所谓封装就是套壳屏蔽细节
OOP全称 Object Oriented Programming
封装: 将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节(对类内部的实现细节进行封装),对外公开接口来和对象进行交互
要实现封装,就需要修改一些数据的权限,修改权限需要
访问修饰限定符
来进行修改.
访问修饰限定符有:private,default,protected,public
private ----- 私有的
只有在当前类的内部进行访问
只有自己知道,其他人都不知道default ----- 默认的
自己家族中(同一个包里面)的可以看到,但是对于其他人来说就是隐私了
包访问权限protected ----- 受保护的
public ----- 公开的
不管在哪里都可以访问
可以理解为一个人的外貌特征,谁都可看到
访问修饰限定符是在访问权限上进行了限制,可以用在成员变量,成员方法以及构造方法上
1.2. 封装的应用
class Date{
private int a ; //类内将数据(private)和方法有机结合,隐藏属性细节
public int getNum() {
return a;
}
public void setNum(int a) {
this.a = a; //对类外提供接口
}
}
public class Test {
public static void main(String[] args) {
Date date = new Date();
date.setNum(18);
System.out.println(date.getNum());
}
}
2. 包
2.1 包的概念
- 为了更好的管理类,吧多个类收集在一起成为一组,即为软件包,类似于目录
- 包是对类,接口等的封装机制的体现,是一种对类或者接口等很好的组织方式
- 在同一个工程中允许存在相同名称的类,只要处在不同的包中即可.
2.2 自定义包
- 在文件的最上方加一个package语句指定该代码在哪个包中
- 包名需要尽量指定成唯一的名字,通常用公司域名的颠倒形式
- 包名要和代码路径相匹配,例如创建
com.wang.www
的包,就会存在有一个对应的路径com/wang/www
来存储代码- 如果一个类没有package语句,则该类被放到一个默认包中.
2.3 常见的包
- java.lang:系统常用基础类(String,Object),此包从jdk1.1后自动导入
- java.lang.reflect:java反射编程包
- java.net:进行网络编程开发包
- java.sql:进行数据库开发的支持包
- java.util:是java提供的工具程序包.(集合类等)
- java.io:I/O编程开发包
要导入一个包中的多个类时,可以直接: import java.util.*
3. static 成员
3.1 static修饰成员变量
- static 修饰的成员变量不在堆区存放,表面上代码位于类内,但该变量实际储存位置位于方法区
- 例如定义一个变量代表学生班级,所有学生在同一个班级时,如果每个学生对象里面都设置一个变量时,就会显得有点多余,这时应该将班级用static修饰
- static修饰的成员不属于对象,属于类,也称为静态成员变量或者类变量
- 一般类中的数据成员设置为private,成员方法设置为public
- static修饰的成员变量,称为静态成员变量,静态成员变量最大的特性:不属于某个具体的对象,是所有对象所共享的
- 即可以通过对象访问,也可以通过类访问,但是一般更推荐使用类名访问
- 类变量存储在方法区中
- 生命周期伴随类的一生,(即:随类的加载而创建,随类的卸载而销毁)
3.2 static成员的初始化
- 当static修饰的变量没有赋值时,默认为null
- 可以就地初始化
- 用类名初始化
Student.class = 21130421;
- Student为类名,类名.class
- 注意: class不在堆区存放,不能用对象.class访问,也不能用this(对象的引用).访问,static修饰方法时同理
- 这也说明了static修饰的成员不属于对象,属于类也称为静态成员变量或者类变量
- 在静态方法内部,不能直接访问非静态的数据成员和成员方法,因为非静态方法有this参数,在静态方法中调用时无法传递this引用,但是非静态方法中可以调用静态方法
- 用代码块初始化
什么是代码块?
4. 代码块
代码块有普通代码块,(实例)构造代码块和静态代码块
以下给出代码块的定义:
普通代码块的定义:
{
//位于方法内
}
实例代码块的定义:
{
//位于方法外
}
静态代码块的定义:
public{
}
- 静态代码块在类加载的时候就会被执行,在没有实例化对象时就已经加载
- 普通代码块是定义在方法中的代码块,普通代码块中的程序执行是没有条件的,可以无视if for循环
- 实例代码块一般用来初始化普通的成员变量
- 静态代码块一般用来初始化静态的成员变量
- 静态代码块不管生成多少个对象,其只会执行一次
- 静态成员变量是类的属性,因此是在JVM加载类时开辟空间并初始化的
- 如果一个类中有多个静态代码块,在编译代码时,编译器会按照定义的先后次序依次执行
- 实例代码块只有在创建对象时才会执行
5. 内部类
内部类有实例内部类,静态内部类,局部内部类,匿名内部类
5.1 实例内部类
实例内部类的定义:
/**实例内部类:
*
*/
class Innerclass{
public int data4;
int data5;
//public static int data6;
}
获取实例内部类的对象:
/**实例内部类:
* 1. 如何获取实例内部类的对象?
* Outclass.Innerclass innerclass = outclass.new Innerclass();
* 2. 实例内部类中不能有静态变量,非要定义,那么只能时被static final修饰的
* public static final int data6 = 18;
* 3. 实例内部类当中获取外部类的this:
* Outer(外部类类名.this.)
*/
package inner;
class Outclass{
public int data1 = 1;
int data2 = 2;
public static int data3 = 3;
/**实例内部类:
* 1. 如何获取实例内部类的对象?
* Outclass.Innerclass innerclass = outclass.new Innerclass();
* 2. 实例内部类中不能有静态变量,非要定义,那么只能时被static final修饰的
* public static final int data6 = 18;
* 3. 在实例内部类当中,如何获取外部类当中相同的变量
* 在实例内部类当中,获取外部类的this:OuterClass.this.data1
*
*/
class Innerclass{
public int data4 = 4;
int data5 = 5;
public static final int data6 = 18 ;
int data1 = 111;
public void out(){
System.out.println(data1);
System.out.println(data2);
System.out.println(data3);
System.out.println(data4);
System.out.println(data5);
System.out.println(data6);
System.out.println(Outclass.this.data1);
}
}
}
public class Test {
public static void main(String[] args){
Outclass outclass = new Outclass();
Outclass.Innerclass innerclass = outclass.new Innerclass();
innerclass.out();
}
}
注意:
- 外部类中的任何成员变量都可以在内部类中直接访问
- 在实例内部类方法中访问同名的成员时,优先访问自己的,如果要访问外部类同名的成员,必须:外部类名称.this.同名成员来访问
- 实例外部类必须在现有外部类对象的前提下才能创建
- 实例内部类的非静态方法中包含了一个指向外部类对象的引用
- 外部类中,不能直接访问实例内部类中的成员,如果要访问必须先要创建内部类的对象
5.2 静态内部类
/* 如何获取静态内部类对象:OuterClass2.Out out = new OuterClass2.Out();
* 静态内部类当中不能访问外部类的非静态成员变量和成员方法,访问需要外部类对象的引用
* 创建静态内部类时不需要创建外部类
* */
package inner;
class OuterClass2{
/* 静态内类对象的引用:OuterClass2.Out out = new OuterClass2.Out();
*
* */
public int data1 = 1;
int data2 = 2;
public static int data3 = 3;
static class Out{
}
}
public class Test2 {
OuterClass2.Out out = new OuterClass2.Out();
}
5.3 局部内部类
- 在方法内定义的类,只能在方法体内使用
- 不能被public和static等访问限定修饰符修饰
5.4 匿名内部类
匿名内部类可以在定义的时候调用类中的方法,匿名内部类一般用来重写方法.
public static void main(String[] args) {
new Test2(){
void func(){
System.out.println("func1");
}
}.func();
}
6. 对象的打印
static class Out{
String name = "zhang";
int age = 19;
@Override
public String toString() {
return "Out{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public static void main(String[] args) {
OuterClass2.Out out = new OuterClass2.Out();
System.out.println(out);
}
二. 继承
1.1 对继承的认识
将多个方法中重复出现的属性和方法抽取出来放到一个类里面,在定义诸多类时继承存放有公共属性和方法的类(对共性的抽取,实现复用).exdents前面的类为子类(又称派生类),后面的类为父类(又称基类或者超类),exdents的出现使得子类和父类之间产生了继承的关系,子类继承父类之后,将会继承父类的全部属性和方法,注意被限定修饰符修饰的方法和属性仍可以被继承,但是是否可以被访问还是要受到限定修饰符的影响.
1.2 继承机制
继承机制:是面向对象程序设计使代码可以复用的最重要的手段,他允许程序员在保持原有类特性的基础上进行扩展,增加新功能,这样产生的类,称派生类,继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程.
1.3 继承的语法
修饰符+子类+extends+父类
java中只能继承一个类,不可继承多个类
1.4 父类成员访问
父类静态代码块优先于子类静态代码块执行
父类实例代码块优先于子类实例代码块执行
子类的实例代码块和子类构造方法紧接着再执行
第二次实例化子类对象时,父类和子类的静态代码块都将不会再执行
当父类与子类的成员属性和方法同名时,优先访问子类的,方法时会判断方法参数从而确定访问父类还是子类,若参数一致,则同样优先访问子类的,如果想在同名情况下访问父类的属性和方法就需要用到super关键字
2.1 super关键字
super并不是父类的引用,而是在代码层面取代了父类的内容,使得代码易读
super只能在非静态方法中使用
super.变量名:访问父类中的普通成员变量
super.方法:访问父类中的普通成员方法
super():访问父类中的构造方法
- 将子类构造之前,要先将父类初始化,在子类中写一个构造方法,参数个数为父类成员方法个数+子类成员方法个数,在构造方法中用super调用父类的构造方法,初始化父类中的成员变量,这里虽然调用了父类的构造方法但是并没有创建父类的实例化对象,这里只是帮助子类来初始化从父类继承过来的属性(将继承的成员构造完)
- super调用父类的构造方法时必须写在子类的构造方法中的第一句,不能和this同时出现
2.2 super和this的异同点
相同:
- 都是关键字
- 都只能在类的非静态方法中使用,用来调用非静态成员方法和字段
- 在构造方法中调用时,都只能在第一行中出现,并且不能同时出现
不同点:
- this是当前对象的引用,当前对象即调用实例方法的对象,而super子类从父类中继承下来的部分引用
- 在非静态方法中,this是用来访问当前类的方法和属性,而super访问的是从父类继承的方法和属性
- 在构造方法中,this用来调用本类的构造方法,super用来调用父类的构造方法
- 构造方法中一定会存在super()的调用,不写时编译器也会默认加上,但是this不会
3.1 继承方式
4.1 final关键字
- 修饰常量
- 修饰类
final class A extends B{
//final修饰的类为密封类,不能被继承,可用于限制继承
}
- 修饰方法,可用于限制方法,表示该方法不能被重写
5.1 继承与组合
https://www.hollischuang.com/archives/1319
//组合的实现
class Student{
}
class Teacher{
}
class School{
Student student[] ;
Teacher teacher[];
}
组合没有过多语法,知识将一个类的实例作为另一个类的字段
三. 多态
多态发生在继承和向上转型的基础上
1.1 重写
重写也称为覆盖,是子类对父类非静态,非final或private修饰,非构造方法的方法重新编写
- 方法重写时,一般必须与父类中方法原型保持一致,即返回类型,参数类型,个数,顺序
- 被重写的方法返回类型可以不同,但必须与父类中方法返回类型有父子关系
- 重写后的方法访问权限必须要大于等于父类中的访问权限
重写和重载的区别:
- 重写一定是发生在继承层面上的
- 重载和重写对返回类型,参数列表,和访问限定修饰符的约束不一样
静态绑定:前期绑定(早绑定)再编译时,根据方法调用时传入参数的个数,类型就能确定要调用哪个方法
动态绑定:后期绑定(晚绑定),再编译时不确定调用哪个类的对象,运行时才确定
1.2 向上转型和向下转型
1.2.1 向上转型
创建一个子类对象,当成父类对象来使用
//Animal为父类类名
//Dog为子类类名
Animal animal = new Dog()
创建子类对象,将子类对象转换为父类引用,大范围囊括了小范围,用父类来实现对子类对象的引用是由小范围向大范围转换,是安全的
以上向上转型所用的是直接赋值的方法,还可以通过传参来实现向上转型
package inherit;
class Animal{
}
class Dog extends Animal{
}
public class Test {
public static void fun(Animal animal){
}
public static void main(String[] args) {
Dog dog = new Dog();
fun(dog);
}
}
引出多态:
同一个引用调用同一个方法,因为引用的对象不一样而表现出不同的行为,这种思想就叫做多态
多态的实现:
public class Test {
public static void fun(Animal animal){
animal.eat();
}
public static void main(String[] args) {
Dog dog = new Dog("小黑", 18);
fun(dog);
Cat cat = new Cat("小花", 18);
fun(cat);
}
}
从上面可以看出,向上转型可以使代码更加整洁,但是向上转型后的对象animal只能调用Animal中的属性或者子类中重写后的方法,不能调用到子类中特有的方法
1.2.2 向下转型
向下转型因为安全性差,万一转换失败,就会抛出异常,所以不常用,为了弥补安全性差的问题,java引入关键字instanceof,若返回值为true则可以安全转换
1.2.2.1 instanceof关键字
if (animals instanceof Cat){}
//判断animals是否引用了Cat这个对象,返回值为true或者flase
2.避免在构造方法中调用重写的方法
在父类的构造方法中避免调用实例方法,因为在子类构造时会进入super()即父类的构造方法,如果父类的构造方法中有实例方法且此方法与子类中的方法同名时(即方法在子类中重写),就会发生动态绑定,从而调用子类中的方法.但是此时子类并没有构造完成,这就可能会引起一些隐藏的难以发现的问题因此要注意避免在父类构造方法中调用实例方法,除非时final或者private修饰的方法