面向对象
一、static
static是什么?
static是一个修饰符,表示静态的意思;
static能修饰什么?
1.修饰类中的变量,成了类的变量,属于类,建议使用类名调用;也可以使用对象调用;
2.修饰方法,属于类,建议使用类名调用;也可以使用对象调用;
3.修饰类中的代码块,成了静态代码块,类在加载的时候,会自动执行一次,给类中的静态成员变量进行赋值;
静态内容总是优先于非静态,所以静态代码块比构造方法先执行。
什么时候可以使用static?
1.定义常用的时候,可以使用static修饰变量;
2.设计工具类中的方法的时候,可以修饰方法,让调用者无需创建对象,直接使用工具类类名调用方法;
3.在某各个类中需要做一次性操作的时候,可以使用静态代码块完成;(例如:mysql驱动注册)
//创建一个Student类
public class Student {
static String name;
int age;
}
//测试
public class Test01 {
public static void main(String[] args) {
//1.static修饰的内容属于类,可以使用类名直接调用
Student.name = "Tom";
Student.name = "Joke";
Student.name = "Nick";
System.out.println(Student.name);
Student s1 = new Student();
s1.age = 22;
Student s2 = new Student();
s2.age = 24;
System.out.println(s1.age);
System.out.println(s2.age);
System.out.println(Student.age); //会报错
}
}
运行结果是:
Nick
22
24
结论:
- 1.类变量:属于类,在内存中只有一份,用类名调用,后面的赋值会取代前面的;
- 2.实例变量:属于对象,每一个对象都有一份,用对象调用
应用场景:
当我们描述某个事物的属性是所有对象共享的时候,可以使用static修饰;例如:描述 大楼的电梯;
static类变量和实例变量的区别
1.static类变量又叫静态成员变量,它不需要创建对象就可以在内存中就存在了;
2.在创建实例对象的时候,内存中会为每一个实例对象的每个非静态成员变量开辟一段内存空间,用来存储这个对象所有的非静态成员变量;
3.static类变量是所有对象共有的,当一个对象将它的值改变,其他对象得到的就是改变后的结果;
4.实例对象属于对象私有,某一个对象将其值改变,不会影响其他对象;
单例模式
懒汉式
在设计成员变量的时候,不存值,只有在第一次获取对象的时候,才创建对象,后续就不再创建了!
步骤:
1: 将类的构造方法私有;
2: 设计一个静态的成员变量,专门用于保存该类的对象;
3: 设计一个静态方法,专门让别人通过这个静态方法,获取对象;
饿汉式
在设计成员变量的时候,直接给成员变量存入对象;
步骤:
1: 将类的构造方法私有;
2: 设计一个静态的成员变量,专门用于保存该类的对象;
3: 设计一个静态方法,专门让别人通过这个静态方法,获取对象;
二、final
final是什么?
是一个修饰符,表示最终的意思;
final能修饰什么?
1.修饰类----->类就不能被继承;
2.修饰方法---->该方法不能被重写;
3.修饰变量
基本数据类型----->数据值不能改变;
引用数据类型----->地址值不能改变,但地址值中的内容可以变;
public class MyFinal {
public static void main(String[] args) {
final int a = 3;
//a = 5;// 数据值不能变;
System.out.println(a);
final double[] arr = {3.3,5.5};
// 创建一个新数组,交给arr
//arr = new double[]{9.9};// 地址值不能变
arr[0] = 8.8;// 地址值中的内容可以变
System.out.println(arr[0]);
}
}
总结:
final一般会配合public和static修饰变量,形成常量,常量名中的所有字母都大写,每个单词之间使用下划线连接;
什么时候可以使用final?
1.如何一个类中的所有方法都不希望别人增强,就可以直接在这个类上加final;
2.如果作为父类,不希望子类重写某个方法,可以在这个方法上加final;
3.设计一些常量的时候,可以使用final,且配合public和static;
三、继承
继承是什么?
是一种可以让类与类之间产生父子关系的技术,子类可以继承父类的代码;
继承有什么好处?
可以提升代码的复用性;
什么时候可以使用继承?
当多个事物有共性内容的时候,可以向上抽取,放到父类中,让所有的子类继承这个父类,就可以共享使用;
public class Fu {
int a = 30;
public void showA(){
System.out.println("父类的a="+a);
}
}
public class Zi extends Fu{
//int a = 20;
public void ziShow(){
System.out.println("子类的show,a="+a);
}
}
public class TestZi {
public static void main(String[] args) {
// 创建子类对象
Zi z = new Zi();
// 这个z对象,既可以调用 Zi类中的内容,也可以调用Fu类中的内容,说明它创建的时候,是利用了Fu+Zi合并的模版
z.showA();
z.ziShow();
Zi z2 = new Zi();
z2.a = 100;// 不管子类是否有a这个变量,所有的对象,都是独享的,互不影响!
z2.ziShow();// 100
z.ziShow();// 20
}
}
方法重写的意义
方法重写是什么?
在子父类中,子类拥有和父类一模一样声明的方法就是重写;
重写的意义?
可以让调用者不改变调用方式的前提下,得到更强大的功能!
方法重写和方法重载的区别
重写: 发生在父子类中,方法名、参数列表必须相同,返回值的返回小于等于父类,抛出的异常小于等于父类,访问修饰符范围大于等于父类,如果父类方法访问修饰符为private ,则子类就不能重写该方法;
重载: 发生在同一个类中,方法名必须相同,参数类型不同,个数不同,顺序不同 ,方法返回值和访问修饰符可以不同,发生在编译时;
构造方法是什么?
是类中特殊的方法,语法格式特殊,没有返回值类型;
格式:
public 类名(形式参数列表){
给类中的成员变量赋值;
}
构造方法有什么用处?
1. 可以创建对象,如果一个类中,不写构造方法,java会赠送一个无参数的构造方法,如果写了,就不赠送了;
2. 可以在创建对象的过程中,给成员变量赋值;
什么时候可以使用它?
只要创建对象,必定会使用到构造方法,如果一个类中所有的构造方法都是private,那么外界是无法直接创建这个类的对象的,内部可以!
this和super的对比
this关键字 | super关键字 |
---|---|
可以区分局部变量和成员变量重名;(this.变量名) | 可以区分子类的成员变量和父类的成员变量重名;(super.变量名)如果私有了,不能这样访问,只能通过getter和setter方法访问; |
可以调用成员方法;this.方法名(实参) | 可以调用成员方法;super.方法名(实参); |
可以调用构造方法;this(实参); | 可以调用构造方法;super(实参); |
//父类
public class Fu2 {
public Fu2(int a){
System.out.println("父类带int参数的构造方法...");
}
}
//子类
public class Zi2 extends Fu2{
//空参构造方法
public Zi2(){
// 调用自己的带int类型参数的构造方法
//super(888);// this调用构造方法和super调用构造方法都只能在第一行,目的是为了保证一个类最多只能有一个亲爹
this(666);
System.out.println("子类空参数的构造方法");
}
public Zi2(int a){
// 最终还是需要手动调用父类可以使用的构造方法
super(a);
System.out.println("子类带int参数的构造方法");
}
}
public class Test {
public static void main(String[] args) {
// 1: 创建子类对象
//Zi z = new Zi();
System.out.println("-----------------------");
Zi2 z2 = new Zi2();
}
}
四、抽象类
抽象方法
当我们无法详细的描述一个方法的具体内容的时候,可以使用抽象方法的形式,仅描述方法的声明,而不写方法体,这样的方法,就是抽象方法;abstract是一个抽象的意思的修饰符;
抽象方法的作用
如果父类中设计了抽象方法,那么将来子类必须重写这个抽象方法,补全方法体,如果不重写,语法错误!(强制子类干某件事)
抽象类
包含了抽象方法的类,一定是抽象类;
抽象类的应用场景(作为模版设计模式使用)
模版设计模式,就是将一个类设计成一个固定的流程,让子类参与流程中具体要做的事情,大体流程由模版把控;
步骤:
1. 写一个抽象类
2. 设计一个模版方法,要求子类一定不能重写该模版方法;(final)
3. 设计一个功能方法,要求子类必须重写该方法,完成具体的功能;(abstract)
五、接口
接口是什么?
是java中的一种引用数据类型;
接口有什么用?
定义接口相当于是制定了"做事情的规范",将来所有具体的事情都可以按照"规范"做事情,可以更好的实现配合;
接口的语法格式
使用 interface 定义接口,使用 implements 表示类实现接口;
六、多态
多态是什么?
一个对象,多种形态;
当一个对象,处于某种形态的时候,只能做与这个形态相关的事情,这就是多态要研究的问题;
多态的代码形式
父类类型 变量名 = 子类类型的对象;
编译看左边(父类),运行看右边(子类);
多态使用的前提
1.必须有继承或者实现;
2.必须有方法重写;
3.必须有父类类型的变量保存了子类类型的对象;
多态入门案列
多态的好处
可以提升代码的扩展性,具体的体现就是在方法的形式参数位置或方法的返回值位置;
多态的弊端
不能直接使用子类中特有的内容;(因为语法编译的时候会检查父类)
多态的转型
当我们需要使用子类特有的内容的时候,可以将多态形式的对象,强制转成子类类型,即可使用了;
避免转型出现类型转换异常
使用 instanceof关键字可以判断一个多态对象的原始类型;
七、匿名内部类
匿名内部类是什么?
就是在一个类的方法内又编写了一个没有名字的类,并且立刻创建出了这个没有名字的类的对象;
语法格式:
new 父类类名(){
// 此时的大括号就是没有名字的类
};
入门案列
匿名内部类的使用前提
只要能找到一个可以做父类的角色就可以使用匿名内部类;
八、Lambda
lambda是什么?
是JDK8提供的一种新的语法格式,这种语法格式,可以简化我们创建对象和编写类和调用方法的语法!
从思想上也做了改变,以前是面向对象解决问题,强调的主题是"对象",必须通过"对象"完成功能;
lambda的语法就弱化了对象,突出了"对象要做的事情",更加倾向于让程序员使用lambda的语法表达出"对象最终要做的事情"即可,至于对象怎么来的,程序员无需关心了!
lambda的使用前提
lambda的宗旨是凡是可以推导的,都是可以省略的!所以使用前提是必须有"推导的依据";依据就是一个接口;
必须有接口,且接口里面有且仅有一个抽象方法,需要子类重写的时候,才可以使用lambda;
lambda的使用前提
()->{};
()就是重写接口中的那个抽象方法后的形式参数列表;
-> 固定格式;
{}就是重写接口中的那个抽象方法后的方法体;
lambda简化规则
1. 当形式参数有且仅有1个的时候,小括号可以省略;
2. 当方法体中有且仅1行代码的时候,return,分号,大括号可以同时省略;
lambda作为方法参数使用
九、枚举
枚举是什么?
是一种特殊的引用数据类型;
格式:
修饰符 enum 枚举类名{
名称1,名称2,...;
其他成员..
}
//byte,short,int,long,字符串String,和枚举
switch(){
}
有什么作用
可以自己提前把所有可能出现的数据都罗列出来,以供其他人使用,增强了代码的可读性和安全性;
应用场景
在设计方法的形参的时候,如果使用枚举可以对调用者传递的数据值进行限制;
十、常见算法
算法是什么
也可以看成是一种解决问题的经验;一套专门针对某一类问题的一个行之有效的方案;
具体有哪些常见的算法?
二分查找
代码思路:
1. 永远使用中间位置的元素和要查询的元素比大小;
2. 如果要找的元素小于中间元素,就让记录最大范围的索引 = 中间索引-1;即在左边
3. 如果要找的元素大于中间元素,就让记录最小范围的索引 = 中间索引+1;即在右边
4. 如果相等,说明找到了,直接返回中间索引即可;
5. 如果表示大范围的索引已经小于了小范围的索引,则停止查找,说明没有这个元素!
代码实现
/*
练习二分查找法找指定元素的索引
*/
public class ErFen {
public static void main(String[] args) {
//定义一个整数数组,前提是已经排过序的
int[] arr = {2,5,8,9,22,77,88,99,222};
Arrays.sort(arr);
/*
//通过这步,可以对数组进行排序
String s2 = Arrays.toString(arr);
System.out.println("排序后:"+s2);
*/
//查找的数组,以及需要查找的元素;
System.out.println(getIndex(arr, 888));
}
public static int getIndex(int[] arr,int key){
// 1: 定义两个变量,分别表示数组的查询范围
int min = 0;
int max = arr.length-1;
// 2: 循环查询
while (min<=max){
// 3: 计算min和max的中间位置的索引是多少
int mid = (min+max)/2;
// 4: 使用 key和mid位置的元素比较
if(key > arr[mid]){
// 说明要查的这个key应该在右边
min = mid+1;
}else if(key < arr[mid]){
// 说明要查的key应该在左边
max = mid -1;
}else {
return mid;
}
}
// 循环结束后,说明找不到
return -1;
}
}
选择排序
步骤:
1. 使用循环嵌套完成排序
2. 外循环的次数,是数组的长度减1次;
3. 内循环,始终使用当前位置的元素,和后面的每一个元素比较,求最小值即可
代码实现
/*
选择排序
*/
public class XuanZe {
public static void main(String[] args) {
int[] arr = {3,6,8,3,5,2,7,1};
xz(arr);
System.out.println(Arrays.toString(arr));
}
public static void xz(int[] arr){
/*
1. 使用循环嵌套完成排序
2. 外循环的次数,是数组的长度减1次;
3. 内循环,始终使用当前位置的元素,和后面的每一个元素比较,求最小值即可
*/
for (int i = 0; i < arr.length-1; i++) {
// 3. 内循环,始终使用当前位置的元素,和后面的每一个元素比较,求最小值即可
for (int j = i+1; j < arr.length; j++) {
// 使用i索引位置的元素,和 j的每个位置的元素都比较一次
if(arr[i] > arr[j]){
// 说明更小的元素出现了,交换位置
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
}
}
冒泡排序
核心思路就是使用相邻的两个元素比较,大的放右边,小的放左边;
代码实现
public class MaoPao {
public static void main(String[] args) {
int[] arr = {1,4,7,2,5,8,3,6,9};
mp(arr);
System.out.println(Arrays.toString(arr));
}
public static void mp(int[] arr){
// 1: 使用循环嵌套完成排序,外层循环-1的目的是为了少循环1次
for (int i = 0; i < arr.length-1; i++) {
// 2: 内循环的次数,需要越来越少,内层循环-1的目的是为了下面 j+1的时候,索引不能越界!
for (int j = 0; j < arr.length-i-1; j++) {
//3: 内循环完成相邻的两个元素比较
if(arr[j] > arr[j+1]){
// 交换
int t = arr[j];
arr[j] = arr[j+1];
arr[j+1] = t;
}
}
}
}
}
十一、Arrays类
Arrays是什么?
是java编写的一个专门处理数组的工具类;
常用方法有
1. 转字符串 toString(数组);
2. 二分查找 binarySearch(数组,元素);
3. 排序 sort(数组,比较器);
4. 修改所有元素 setAll(数组,lambda);
参考代码
/*
练习java提供的数组工具类,完成对数组内容转字符串,数组排序,数组元素查找,数组元素修改!
*/
public class MyArrays {
public static void main(String[] args) {
// 1: 工具类中的方法都是静态方法,所以可以直接使用类名调用方法
//创建一个静态的整数数组
Integer[] arr = {5,8,9,3,5,7,2};
String s = arr.toString();// 虽然语法不报错,但是我们看不到数组中的元素值,
System.out.println(s);
// 可以利用Arrays提供的静态方法toString帮我们把数组转成字符串即可
String s1 = Arrays.toString(arr);
System.out.println(s1);
//利用sort(数组)方法对数组里的内容进行排序
Arrays.sort(arr);
String s2 = Arrays.toString(arr);
System.out.println("排序后:"+s2);
// 必须是有序的数组,才可以使用这个二分查找法查找
int i = Arrays.binarySearch(arr, 5);
System.out.println(i); //数组中的元素5,所在的索引
/*Arrays.setAll(arr,*//*可以写一个lambda作为该方法的第2个实际参数*//*
new IntFunction<Integer>(){
@Override
public Integer apply(int value) {
//System.out.println(value+"----------------------------");
return arr[value]*2;
}
});*/
// 使用lambda作为方法的第二个参数,将数组中的每个元素都放大3倍
Arrays.setAll(arr,ind->arr[ind]*3);
String s3 = Arrays.toString(arr);
System.out.println("修改数组的元素后:"+s3);
}
}
自定义排序规则的套路
排序规则永远使用减法即可!减法的结果是正数,java就认为第2个数比第1个数大,(就是升序),减法的结果是负数,则认为第2个数比第1个数小,则是降序;
记结论:
只要想要升序的效果,就使用参数1减参数2;
只要想要降序的效果,就使用参数2减参数1;
排序的代码永远都可以使用 比较器;
Arrays.sort(数组名,(a,b)->b-a或a-b);
public class Student implements Comparable<Student>{
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int compareTo(Student o) {
//return this.age-o.age;
return o.age-this.age;
}
}
/*
对数组存学生进行排序
*/
public class MyArrays3 {
public static void main(String[] args) {
// 1: 创建一个保存4个学生对象的数组
Student[] arr = new Student[4];
arr[0] = new Student("迪丽热巴",18);
arr[1] = new Student("古力娜扎",19);
arr[2] = new Student("玛尔扎哈",17);
arr[3] = new Student("马卡马卡",28);
Arrays.sort(arr);
for (int i = 0; i < arr.length; i++) { System.out.println(arr[i].getName()+"===>"+arr[i].getAge());
}
}
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int compareTo(Student o) {
//return this.age-o.age;
return o.age-this.age;
}
}
/*
对数组存学生进行排序
*/
public class MyArrays3 {
public static void main(String[] args) {
// 1: 创建一个保存4个学生对象的数组
Student[] arr = new Student[4];
arr[0] = new Student(“迪丽热巴”,18);
arr[1] = new Student(“古力娜扎”,19);
arr[2] = new Student(“玛尔扎哈”,17);
arr[3] = new Student(“马卡马卡”,28);
Arrays.sort(arr);
for (int i = 0; i < arr.length; i++) { System.out.println(arr[i].getName()+"===>"+arr[i].getAge());
}
}
}