目录
Object Oriented Programming的三大特性之一之封装性
多态和方法的覆写与对象向上转型时发生的动态绑定和对象的向下转型
数组的存储结构以及类库支持
1.一维数组
- 数组中存放的是一组数据类型相同的数据元素,数据元素在内存中存放的地址是连续的。Java中定义数组的三种方式:
int[] arr = {1,2,3,4,5}; int[] arr = new int[] {1,2,3,4,5}; //数组的静态初始化 int[] arr = new int[5]; //数组的动态初始化,数组中的元素默认为0
- 数组作为引用数组类型,数组名本质上是对new关键字在JVM虚拟机的堆上开辟的空间的地址的引用。
- 数组的两种输出方式
for(int i=0;i<arr.length;i++) {} //常用于输出数组中的特定元素 for(int val : arr) {} //常用于遍历数组
- 对于Java语言来说,无法取得变量在栈上的地址。可以取得栈空间上引用变量引用的堆空间的地址,但是该地址并不是引用变量引用的堆空间的真实地址,是经过哈希处理后返回的地址,这也是Java语言安全性的体现。
public class ArrayTestDrive { public static void main(String[] args) { int[] arr = new int[10]; System.out.println(arr); } } //输出:[I@1b6d3586 //这个值是唯一的,可以看成是arr引用变量引用堆空间的地址,但是并不是真正的堆空间地址
- 数组的引用传递
数组在函数传参过程中传输的是引用变量的值,即数组名。调用函数时形参会在栈上开辟一块临时空间,存放一份拷贝实参的引用值。在函数调用结束后,该形参在栈上开辟的临时空间被释放。public class ArrayTestDrive { public static void printArr(int[] arrTemp) { //接收传递过来的数组 for(int val : arrTemp) { System.out.print(val + " "); } } public static void main(String[] args) { int[] arr = new int[]{1, 2, 3, 4, 5}; printArr(arr); //数组名 } }
-
数组的排序,赋值,输出的类库支持
①排序和输出的类库支持:java.util.Arrays。
排序:sort(arr)/输出(字符串形式):toString(arr)
②复制类库支持:java.lang.System/java.util.Arrays
复制:arraycopy(src arr,srcPos index,dest date,destPos index,length);
复制:copyOf(arr,length);import java.util.Arrays; //导入Arrays类 public class ArrayTestDrive { public static void main(String[] args) { int[] arr = {9,8,7,6,5,4,3,2,1}; int[] date = {10,20,30,40,50}; Arrays.sort(arr); //数组排序结果:arr数组:[1,2,3,4,5,6,7,8,9] for(int val : arr) { System.out.print(val + " "); } System.out.println(); System.out.println(Arrays.toString(arr)); //以字符串的形式输出数组arr:[1,2,3,4,5,6,7,8,9] System.arraycopy(arr,2,date,1,3); //数组拷贝,结果:date数组:[10,3,4,5,50] System.out.println(Arrays.toString(date)); } }
-
二分查找类库支持
java.util.Arrays.binarySearch(arr,valSearch)
import java.util.Arrays; public class BinarySearchClassTestDrive { public static void main(String[] args) { int[] arr = {2,5,8,4,1,3,9}; Arrays.sort(arr); System.out.println(Arrays.binarySearch(arr, 8));//返回查找到的元素的下标 } }
-
Arrays工具类常用常用方法
①Arrays.copyOf(arr,length)
Arrays.copyOf(arr,from,to)。拷贝的数组范围不包含to的下标值。
②Arrays.sort(arr)
③Arrays.binarySearch(arr,[fromIndex,toindex],valSearch),[fromIndex,toIndex)
当使用binarySezrch()方法中的fromIndex,toIndex参数时,查找的范围不包括toIndex下标的值。
④Arrays.toString(arr)
⑤Arrays.fill(arr,[fromIndex,toIndex],val):以值val填充数组的元素import java.util.Arrays; //导入Array工具类 public class ArrayFillTestDrive { public static void main(String[] args) { int[] arr = new int[8]; Arrays.fill(arr,1,3,9); //不包括下标为3的存储单元 System.out.println(Arrays.toString(arr)); } } //输出:[0, 9, 9, 0, 0, 0, 0, 0]
2.二维数组
-
二维数组的定义方式
int[][] arr = {{1,2,3},{4,5,6}}; int[][] arr = new int[][] {{1,2,3}.{4,5,6}}; int[][] arr = new int[2][3];
-
二维数组的三种打印方式
public class DimensionalArrayTestDrive { public static void main(String[] args) { int[][] arr = {{1, 2, 3}, {4, 5, 6}}; //打印方式一 for (int i = 0; i < arr.length; i++) { for (int j = 0; j < arr[i].length; j++) { System.out.print(arr[i][j] + " "); } } System.out.println(); //打印方式二 for (int[] date : arr) { for (int temp : date) { System.out.print(temp + " "); } } //打印方式三 System.out.println(Arrays.deepToString(arr)); } }
类和对象
引用变量和对象之间的关系以及成员变量和局部变量的存储
-
引用变量和对象之间的关系以及成员变量和局部变量的存储
①引用变量:使用new关键字实例化类在堆上开辟的存储空间的引用,引用变量在栈上开辟内存空间。
②对象:类的实例化,对象在堆上开辟内存空间,通过栈上的引用变量来进行调用。
③成员变量:在类中定义的变量称为成员变量或字段,成员变量的存储空间在类实例化出的位于堆上的对象中。成员变量可以不进行初始化,默认值为对应的0值。数值类型为0/0.0,布尔类型为false,char类型为'\u0000',引用类型为null。(static定义的变量也相同)
④局部变量:在方法中定义的变量,局部变量在使用前必须进行初始化。
⑤引用变量和对象之间的关系:
static关键字的使用以及内存分布
- static关键字修饰成员变量
成员变量通过static关键字修饰后称为静态成员变量,该成员变量位于JVM上的方法区内,为该类的所有实例化对象所共有。在访问时通过类名访问。 - static关键字修饰方法
①通过static关键字修饰的方法称为静态方法,在调用时通过类名调用。
②在静态方法内部不能调用非静态的成员变量以及方法。
Object Oriented Programming的三大特性之一之封装性
- 为什么要进行类的封装?
①提高安全性,避免用户对类中的属性进行不合理的操作,安全性好。//对于类中private定义的属性age,可以有: public void setAge(int age) { if (age < 18) { System.out.println("年龄不能小于18岁"); } else { this.age = age; } }
②可维护性好,方便重用。在有需要更改的时候,只需要对内部数据和结构进行更改,不改变外部引用的接口,提高了程序的可维护性,减少对外部引用该类的程序的影响。
-
类的封装原则
①将不需要对外提供的功能以及实现细节都隐藏起来
②将属性都隐藏起来,提供公共方法供外部调用 -
类封装属性和获取类详细信息的快捷键(Intellij IDEA)
①Ait + Insert --> getter and setter --> 勾选需要进行封装的属性
②获取对象详细信息的toString()方法:Alt + Insert --> toString -->选择要进行输出的属性
类的构造方法以及this关键字的使用
- 什么是构造方法?构造方法的定义规则
构造方法是在类内定义为类创建对象所服务的。
定义规则:
①构造方法的方法名和类名相同且无返回值。
②构造方法可利用oop的多态性定义多个,供实例化对象使用。
③如果不进行构造方法的自定义,Java在new对象是会默认调用一个无参的构造方法;当有自定义构造方法时,Java只会执行已经被自定义的构造方法,默认的无参构造方法失效. - this是指当前对象,这句话是错误的。
可以从构造方法的角度来解释,在使用构造方法初始化成员变量时使用this时,这是的对象还没有产生,因此this只能是对当前对象的引用,而不是当前对象。 - this关键字的使用
this.属性 对当前对象的属性的引用 this.方法() 对当前对象的方法的调用 this() 在构造方法内部调用其他重载的构造方法,不能调用默认的无参构造方法,且在构造方法内部只能出现一次且必须在构造方法体的首行书写。
代码块以及代码块的执行顺序
- 主要代码块的分类
①实例/构造代码块:定义在类中的非static关键字修饰的代码块,在类通过new关键字实例化对象时执行,且会根据实例化的次数执行多次。
②静态代码块:在类中通过static关键字定义的代码块,静态代码块在类加载时被执行且只被执行一次。静态代码块总是最先执行,且主类中的静态代码块的执行优先于非主类中的静态代码块的执行。
③普通代码块:定义在方法中,起到将代码块进行分割的作用class CodeBlock { private String code; public static int count; { //实例/构造代码块 System.out.println("构造代码块执行..."); } static { //静态代码块的执行优先级最高 count = 99; System.out.println("静态代码块执行..."); } public void setCode (String code) { this.code = code; { System.out.println("普通代码块执行..."); } } } public class CodeBlockTestDrive { static { System.out.println("主类中的静态代码块执行..."); //主类中的构造代码块总是最先被执行 } public static void main(String[] args) { CodeBlock code = new CodeBlock(); code.setCode("public static void main(String[] args)"); } } //执行结果: 主类中的静态代码块执行... 静态代码块执行... 构造代码块执行... 普通代码块执行...
-
相同类型的代码块的执行先后顺序
代码块类型相同时,写在前面的代码块优先被执行。静态代码块在类实例化时,只执行一次。
匿名对象
- 没有引用的对象称为匿名对象
- 匿名对象只能在创建对象时使用
- 如果一个对象只使用一次,后面不需要了,可以考虑使用匿名对象
Java中的包机制
- 为什么要使用包?
在公司的一个项目开发过程中,通常是多人协同开发的,那么就可能出现不同人命名名称相同的class文件,包机制的出现很好的解决了这种问题,为项目的团队开发提供了便利,同时包机有利于项目代码的分区管控和后期维护。
①解决了不同开发人员java文件命名冲突的问题。
②方便代码的分区管控,方便后期维护。 - 包的命名规则
①公司通常采用域名的倒置作为包名:com.baidu.www
②包命名时要求所有的字母都要小写。 - 关于包的定义和包的导入
①定义:Java中所谓的包实际上指的就是系统中的文件夹,在Java中使用package关键字定义包,此语句必须编写在代码的首行。定义包的命令行代码://假设Hello文件下package com.shuai.www; 编译文件生成包:javac -d . Hello.java 运行程序(必须包含文件包名):java com.shuai.www.Hello
②导入:当一个类需要使用不再同一个包下的类时,需要通过import关键字进行包的导入
继承的规则以及隐式继承
- 什么是继承以及继承的作用?
继承是发生在oop对象上的一种现象,继承性是Object Oriented Programming的三大特征之一。对象的继承解决了项目开发过程种代码重复书写的问题。严格来讲,继承性是当父类的功能无法满足子类模块功能开发需要时,对子类的扩充手段。 - 子类都继承了父类的什么?
子类继承了父类的所有属性和除了构造方法外的所有方法。子类在构造时,必须有构造方法对应父类,父类完成构造后才能完成子类的构造。 - 什么时显式继承和隐式继承?
①显示继承:对于所有非私有的操作,属于显式继承
②隐式继承:对于所有私有操作属于隐式继承。
四种访问权限修饰符
No. | 访问范围 | private | default | protected | public |
1. | 同一包的同一类 | √ | √ | √ | √ |
2. | 同一包的不同类 | √ | √ | √ | |
3. | 不同包的子类 | √ | √ | ||
4. | 不同包的非子类 | √ | |||
5. | 修饰对象 | 属性和方法 | 属性和方法、类 | 属性和方法 | 属性和方法、类 |
final关键字的使用以及注意事项
- final修饰变量:该变量称为常量,只能在初始化时复制,后期不可被更改。
- final修饰类:该类不能被继承,称为密封类。
- final修饰方法:该方法不可以被覆写,称为密封方法。
组合的使用
- 组合并没有涉及到特殊的语法,仅仅是将一个类的实例作为另一个类的字段,例如要设计一个学校类--学校里边又有学生和老师,这时候如果使用继承就无法表达清楚学生,老师和学校之间的关系。
class Stu {} class Teacher{} public class School{ public Stu[] students; public Teacher[] teachers;//将一个类的字段放当做另一个类的成员属性 }
多态和方法的覆写与对象向上转型时发生的动态绑定和对象的向下转型
- 方法的重写
①规则:
方法的返回值,方法名称以及方法的参数列表包括参数类型以及个数完全相同
子类所覆写的父类的方法的访问权限修饰符的权限必须大于等于父类方法中的权限。
②方法的覆写与方法的重载之间的区别:覆写和重载 发生位置 规则 覆写Override 父类被继承的子类当中 返回值,参数列表,方法名完全相同 重载Overload 发生在同一个类中 方法名相同,参数列表不同,返回值和权限不做要求 - 对象向上转型:继承自父类的对象可以实例化父类对象,称为对象向上转型。对象向上转型有三种方式:直接赋值、函数返回值、函数参数。
- 向上转型时发生的动态绑定
当子类中重写了父类中的方法时,子类向上转型实例化父类后,如果子类中对父类中已有的方法进行了覆写,那么通过父类实例调用的方法为子类覆写过的方法。class Animal { private String name; public void eat() { System.out.println("Animal类eat()"); } } class Cat extends Animal{ public void eat() { System.out.println("Cat类eat()"); } } public class DecompileTestDrive { public static void main(String[] args) { Animal animal = new Cat();//对象向上转型实例化父类对象 animal.eat(); //输出:Cat类eat() } }
-
多态向上转型时发生的动态绑定或称运行时绑定与反编译
①子类覆写父类中的方法后。通过该子类实例化的父类对象调用该方法时在编译时仍编译的是父类中的方法,在执行时执行的是子类中的方法,该现象称为动态绑定或运行时绑定
②反编译的步骤 -
向下转型以及注意事项
①当子类中扩展了新的方法,那么通过子类向上转型实例化的父类对象就无法调用该子类中的方法,可以通过父类向下转型来访问子类中扩充出的方法。class Animal { private String name; public void eat() { System.out.println("Animal类eat()"); } } class Cat extends Animal { public void eat() { System.out.println("Cat类eat()"); } public void print() { System.out.println("加油"); } } public class DecompileTestDrive { public static void main(String[] args) { Animal animal = new Cat();//子类对象向上转型实例化父类对象 // animal.print(); 该父类对象无法调用子类中扩展出的新方法 if (animal instanceof Cat) { //判断animal是否是Cat类的实例 Cat cat = (Cat) animal; //父类向下转型后调用子类中扩展出的方法 cat.print(); } } }
②父类向下转型具有极大的不安全性
当还有一个Bird类继承Animal类时,通过Cat类实例化了一个Animal对象animal,那么此时将animal对象向下转型为Bird对象时在程序编译时并不会出现问题,而在程序执行期间就会出现"ClassCastException"异常。
在构造方法中也会发生动态绑定
- 当在父类的构造方法中调用一个被子类覆写过的方法,那么执行的是子类覆写过的方法,此时发生了动态绑定。
class Animal { private String name; public Animal(String name) { this.name = name; eat(); } public void eat() { System.out.println("Animal类eat()"); } } class Cat extends Animal { public Cat(String name) { super(name); } public void eat() { System.out.println("Cat类eat()"); } } public class DecompileTestDrive { public static void main(String[] args) { Animal animal = new Cat("可爱");//子类对象向上转型实例化父类对象 } } //输出:Cat类eat()