目录
Java中的JDK、JRE、JVM:
JDK:Java Development Kit(Java开发工具包)
JRE:Java Runtime Environment(Java运行环境)
JVM:Java Virtual Machine(Java虚拟机)
JDK中包含有JRE,JRE中包含有JVM,所以呢我们不需要单独去安装JRE,只需要安装部署好JDK的版本以及运行环境即可
八种数据的基本类型:
byte --> short --> char --> int --> long --> float -- > double
boolean布尔类型
char类型的本质其实就是Unicode编码还是数字
除了这八种基本类型,其他以外的数据类型都可以称作为引用类型
数据类型的两种转换方式:
1、自动转换:将低位数据类型转换为高位时,会自动的转换,精度不会丢失。比如:int a = 1; double b = a;
2、强制转换:将高位的数据类型转换成低位时,需要强制转换。比如:double b = 3.1415926; int a = (int)b;
而且强制转换可能会导致精度的丢失,小数转整数就不说了因为肯定会丢失,当整数转整数的时候可能会因为数据内存的溢出而导致转换失败。比如说当我们将int类型的数据强制转换为byte的时候我们要明白int是4字节,而byte是1字节,当数据的二进制位数超过8位的时候将会转换失败。
数据的运算:
算数运算符:+ - * / % ++ --
赋值运算符:=
关系运算符:< > <= >= == != instanceof
逻辑运算符:&& || !
位 运 算 符:& | ^ ~ >> << >>>
条件运算符:?:
拓展赋值运算符:+= -= *= /=
这里重点来说说位运算符:
&表示都是1则为1,有0则为0 (与)
| 表示都是0则为0,有1则为1 (或)
^ 表示相同则为0,不相同则为1(异或)
~表示全部取反
比如:A = 0011 1100
B = 0000 1101
A & B = 0000 1100
A | B = 0011 1100
A ^ B = 0011 0001
~B = 1111 0010
四种变量:
局部变量:定义在方法中,作用域只限于方法内使用,使用时需要初始化
实例变量:定义在类中,需要 new 一个对象来调用该变量,但是实例变量不需要初始化就能够使用,因为 Java 会赋给他默认的值,比如 string 类型默认值是 null,int类型默认值是0
类变量:也是定义在类中,用 static关 键词修饰,使用时直接使用,也不需要初始化就能使用
常量:定义时用 final 关键字修饰,常量初始化赋值之后不可修改
Java中的选择结构:
if 选择结构:if、else if、else
switch case 选择结构:在JDK7之前Switch还不能够比较String类型的数据,但是在这之后的版本中Java通过HashCode编码的方法去比较实现了String类型的比较
Java中的循环结构:
while循环:先进行条件判断,再执行语句
do while循环:先执行语句,在进行条件判断,也就是说该循环至少会执行一次
for循环:该循环相较于其他循环是 最常用、最灵活、最高效的一种循环结构
增强for循环:
int[] arry = {10,20,30,40,50,60};
for(int a : arry){}
在循环的过程中我们可以通过 break 和 continue 来控制循环的进行或者结束
可变参数:
当我们需要在形参中传递多个相同类型参数的时候可以使用可变参数 int... 如下:
他会以一个数组的形式将参数存储到数组之中。
public int add(int a,int... b) {
return a+b[0];
}
递归方法:
递归就是方法调用方法本身,比如以下这个计算阶乘的方法,如下:
public static int f(int n) {
if(n = 1) {
return 1;
}else {
return n * f(n-1);
}
}
注意:使用递归方法时注意一定要包含以下两个部分:
递归头:什么时候不调用自身方法。如果没有头将会陷入死循环
递归体:什么时候需要调用自身方法
由于我们Java都是使用栈机制的,所以当计算公式十分繁琐复杂 或者 嵌套层次比较深 的时候我们去调用递归可能会导致内存崩溃,因为递归会产生大量的函数调用(所以在Java中调用递归前提条件是数据基数较小才可以)
Java的内存:
堆:
存放new的对象和数组
可以被所有的线程共享,不会存放别的对象引用
栈:
存放基本变量类型(会包含这个基本类型的具体数值)
引用对象的变量(会存放这个引用在堆里面的具体地址)
方法区:
可以被所有的线程共享
包含了所有的class 和 static 变量
Java数组:
int[] arry = new int [10];
当我们执行 int[] arry 这段代码的时候只是声明了一个数组,会在 栈 中压入一个 arry 但是还没有分配任何的空间或者内存
当我们执行 new int[10] 这段代码的时候就会创建一个数组,会在 堆 中给 arry 数组分配 10 个空间,如果我们没有初始化数组,java则会给数组里每个空间分配一个默认值,如果是 int 类型的数组默认值就是 0
冒泡排序,时间复杂度 O(n^2) :
从数组中第一个元素开始,两两比较如果后面的元素大于前面的元素则交互位置(每轮元素两两比较会比较 arry.length-1 次),由于每轮比较都会将该轮最大的元素放到数组末端,所以下一轮比较一定会比上一轮比较要少一次,最终可以得出冒泡排序总共应该执行 arry.length-1轮
稀疏数组:
当一个数组中大部分元素为0,或者为同一值时,可以使用稀疏数组来保存该数组。
稀疏数组的处理方式是:
记录数组一共有几行几列,有多少个不同值。
把具有不同值的元素的行列值以及值记录在一个小规模的数组中,从而缩小程序的规模。
比如说我们有一个11行11列的数组arry1[11][11],但是其中只有两个有效值arry1[1][1]和arry[2][2]的值为非0,其他位置都是0,那么我们就可以用稀疏数组来压缩该数组达到节省空间的作用。那么稀疏数组为arry2[有效值数量+1][3],数组arry2[0][0]=arry1的行数,arry2[0][1]=arry1的列数,arry2[0][2]=arry1的有效值数,然后接下来就是记录每一个有效值所在的行列以及他的值。
面向对象 & 面向过程:
面向过程思想:
步骤清晰简单,第一步做什么,第二步做什么......
面向过程适合处理一些较为简单的问题。
面向对象思想:
物以类聚分类的思维模式,思考问题首先会解决问题需要哪些分类,然后对这些分类进行单独思考。最后,才对某个分类下的细节进行面向过程的思索
面向对象适合处理复杂的问题,适合处理需要多人协作的问题!
面向对象编程的本质就是:
以类的方式组织代码,以对象的形式封装数据
对于描述复杂的事物,为了从宏观上把握、从整体上合理分析,我们需要使用面向对象的思路来分析整个系统。但是,具体到微观操作,仍然需要面向过程的思路去处理
方法的调用:
静态方法可以直接用 类.方法名 去调用就可以了,因为静态方法和类是同时加载的,类存在的时候静态方法就已经存在了,但是非静态方法要在类实例化之后才会存在,所以无法直接调用。
在Java中都是值传递,举个例子代码如下:
public class Demo() {
public static void main(String[] args) {
int a = 1;
System.out.println(a);
Demo.change(a);
System.out.println(a);
}
public static void change(int a) {
a = 10;
}
}
这两个输出都是1,为什么呢? 因为在Java中都是值传递,然后虽然调用了change()方法,但是他并没有改变main主函数中变量a的值,只是将变量a的值传递到change()方法中执行了一遍change()方法体而已。
类和对象:
类是一个抽象的概念,是一种事物整体的描述或者是定义,而对象是一种具体事物的描述
new 出一个对象之后会分配内存空间以及对其进行默认的初始化 以及 对类中构造器的调用,只要你属于这个类那么就一定会拥有这个类的所有特征和属性,所以可以通过这个对象去修改或执行他的属性值或方法。
构造器详解:
在实例化一个类之后,如果这个类没有定义构造函数,那么Java就会自动给这个类添加一个默认的无参构造函数。
但是如果我们定义了一个有参的构造函数,又想调用无参构造函数的时候,那么就必须显式的定义一个无参构造函数否则实例化会报错。
构造函数的作用:
1.使用new关键字,本质是在调用构造器
2.用来初始化值
从内存上来简单的理解一下 new 的概念:
当我们声明一个对象的时候,Java会把这个对象名存到栈中(也可以说是引用变量名),此时没有分配任何空间,当我们 new 完对象实例化类会后会在堆中给我们分配一块空间,每一块空间都有一个相应的地址,当我们通过引用变量名去修改属性或者调用方法的时候会去找到这一块相对应的地址空间然后进行修改或者调用。
堆中有一块特殊的区域叫做方法区
Java中封装的概念:
该露的露,该藏的藏:
我们程序设计要追求“高内聚,低耦合”,高内聚就是类的内部数据操作细节自己完成,不允许外部干涉;低耦合:金宝路少量的方法个外部使用。
封装(数据的隐藏):
通常,应禁止直接访问一个对象中数据的实际表示,而应通过操作接口来访问,这称为信息隐藏。
其实在开发中最常见的数据封装就是我们的pojo实体类,实体类中的属性为私有,以及他们都会有统一的接口set和get的方法。
再来说说数据封装的好处:
1、提高了程序的安全性,保护数据
2、隐藏了代码的实现细节
3、统一了接口
4、增加了系统的可维护性
继承:
继承的本质是对某一批类的抽象,从而实现对显示世界更好的建模。
extend的意思是“扩展”。子类是父类的扩展
JAVA中类只有单继承,没有多继承!
继承是类和类之间的一种关系。除此之外类和类之间的关系还有依赖、组合、聚合等。
继承关系的两个类,一个为子类(派生类),一个为父类(基类)。子类继承父类,使用关键字extends来表示。
子类和父类之间,从意义上讲应该具有“is a”的关系
所有的类都会继承一个叫做 object 的父类。
Super关键字:
在继承了一个父类之后,我们的子类就会继承父类所有可继承的属性以及方法,并且用super关键字就可以调用,比如 : super.父类方法名()、super.父类属性名 都可以 。super关键字其实就是父类的对象
当 new 出一个对象的时候,其父类的构造函数会先执行,然后才会去执行子类的构造函数。因为在我们子类的构造函数中的第一句中默认包含了一句代码 super(); ,这段代码的含义就是去调用父类的构造函数,而且 super(); 这句话必须写在子类构造函数的第一句。
this关键字:
this关键字用于本身调用者的对象,也就是本身这个类的对象。
方法的重写:
先来看看以下代码:
A.java文件:
public class A extend B {
public static void test() {
System.out.println("A=>test");
}
}
B.java文件:
public class B {
public static void test() {
System.out.println("B=>test");
}
}
Application.java文件:
public class Application {
public static void main(String[] args) {
A a = new A();
a.test();
B b = new A();
b.test();
}
}
可以看到我们的主函数中 声明了两个对象 a 和 b 但是指向的地址都是 A,但是执行后的结果却是 A类 和 B类 中的 test() 方法都执行了。因为父类的引用变量可以指向子类,可以发现如果是 static 修饰的静态方法,无论是用对象a调用,还是对象b调用都可以调用出自己的方法。
但是我发现如果把 static 去掉则会出现 a 和 b 都时调用 A 的方法。静态方法的重写没有任何关系,必须是非静态的方法才能重写。
在最后总结一下:
1、重写方法时,必须要有继承的关系,子类重写父亲的方法
2、方法名必须相同
3、修饰符:范围可以扩大但是不能缩小:public > Producted > Default > private
4、抛出的异常:范围、可以缩小、但是不能扩大:ClassNotFoundEeception-->exception
多态:
即同一方法可以根据发送对象的不同而采取多种不同的行为方式。
一个对象的实际类型是确定的,但可以指向对象的引用类型有很多(父类,有关系的类)
多态的存在条件:
有继承关系
子类重写父类方法
父类引用指向子类对象
注意:
多态是方法的多态,属性没有多态性
之前我们说过 父类的引用可以指向子类 例如:Person p1 = new Student();
对象能调用哪些方法 和 对象左边的类型有关系,和右边的类型关系不大!
所以子类只能调用子类本身存在的方法以及从父类中继承的方法,而父类也只能调用自身的方法而不能去调用子类独有的方法。
instanceof关键字:
用 X instanceof Y 可以判断 对象X 与 类Y 是否有继承的关系,有则返回 true,无则返回 false
子类转换为父类:向上转型可以自动转换,会丢失子类中独有的方法
父类转换为子类:向下转型需要强制转换,会丢失父类中被子类重写的方法
java类中的代码快(补充讲解):
匿名代码块:在这之中的代码执行顺序在 构造方法 之前,当我们类实例化的时候会执行
静态代码块:由于是静态的代码块,所以会和类同时加载,执行顺序在构造方法之前,当然静态的只会执行一次,当我们再次实例化的时候将不会执行
抽象类:
用 abstract 修饰的类叫做抽象类,有抽象方法的一定是抽象类(那么在我们的Java中只有单继承,也就是说一个子类最多只能有一个父类)
抽象类不能实例化也就是不能new,只能靠子类去实现他:只是起到约束的作用
抽象方法:
用 abstrac 修饰的方法叫做抽象方法,抽象方法只有方法的名字而没有方法的实现
抽象方法必须写在抽象类中,抽象类中可以写普通的方法
抽象类的子类:
当我们用子类去继承抽象类的时候,如果想让子类变成非抽象类就需要将父类中所有的方法重写并实现。
抽象类存在的意义:
提高开发的效率,将重复使用的方法抽象抽取出来,每次只需要继承这个抽象类再将需要重写的方法重写即可。
接口:
在接口中定义的所有 属性 默认为静态常量: public static final(但是一般不会在接口中定义常量)
在接口中定义的所有 方法 默认为抽象方法: public abstract
接口和抽象类不同,实现类可以实现多个接口
Java内部类:
内部类可 以调用 外部类 的私有属性和方法
在一个 Java类 中可以有多个 class 但是只能有一个 public class
什么是异常?
Error 和 Exception 的区别:
Error通常是灾难性的致命错误,是程序无法控制和处理的,当出现这些异常时,Java虚拟机(JVM)一般会选择终止线程;
Exception 通常情况下是可以被程序处理i的,并且在程序中应该尽可能的去处理这些异常
异常处理机制:
异常处理的五个关键字:try、catch、finally、throw、throws
try 视监控区域里面放置的代码都会受到监控,当出现 catch 中类型的异常时就会将其捕获
catch 中存放的是异常的类型,
finally 一般用于处理善后工作,无论是否捕获到异常都会执行 finally 中的代码
Java把异常当做对象来处理,并定义了一个基类 java.lang.Throwable 作为所有异常的超类,也就是说 Throwable 包括了所有的异常类。
所以说在前面的 catch 中不要直接放这个超类,否则他捕获了这个超类的异常之后就不会再去捕获其他类型的异常了,因为只会执行一个 catch,所以我们应该层层递进,把这个超类放到最后一个 catch 中
在catch方法体中 e.printStackTrace(); 可以打印出相应的异常信息
throw 和 throws关键字用于抛出异常:
throws用在方法上,假设这个方法中处理不了这个异常,就在方法上抛出异常,
throw 用在体中主动抛出异常
try catch 是遇到错误也能继续运行,而 throw 和 throws 是运行的时候给你抛出异常 然后让catch 捕获这个异常
补充:在 IDEA 中选中代码 Ctrl + Alt + T 选中想要的即可
自定义异常类:
第一步:首先我们得创建一个类然后让这个类 MyException 继承 Exception 代码如下:
//自定义的异常类
public class MyException extends Exception {
//传递数字>10
private int detail;
public MyExcetion(int a) {
this.detail = a;
}
//toString:异常的打印信息
@Override
public String toString() {
return "MyException{" + detail + '}';
}
}
第二步:再去创建一个 测试类 Test.java 如下:
public class Test {
static void test(int a) throws MyException {
System.out.println("传递的参数为:" + a);
if(a > 10) {
throw new MyException(a);
}
System.out.println("OK");
}
public static void main(String[] args) {
try {
test(11);
} catch(MyExcetion e) {
System.out.println("MyException=>" + e);
}
}
}
由于传递的值是 11 大于 10 所以抛出了异常并且被 catch 捕获到了所以后台会打印出 toString 中的信息