开篇先放下个人的Github地址:masuo777.github.io/ 欢迎访问!!!
运算
参数传递
在Java中,参数是以值传递的方式传入方法中,而不是引用传递。
package JavaBase;
/**
* @author masuo
* @create 2021/7/8 23:01
* @Description java中的参数传递 是值传递
*/
public class ParmeterPass {
public static void main(String[] args) {
// 此时的dog是一个指针,存储对象的地址,
// 在将一个参数传入一个方法时,本质上是将对象的地址以值的方式传递到形参中。
Dog doga = new Dog("doga");
changeDogName(doga);
System.out.println(doga.getName());//dogb
System.out.println(doga.getAddress());//JavaBase.Dog@1b6d3586
changeDogAddress(doga);
System.out.println(doga.getName());//dogb,这里名称没变是因为方法中的对象对此处无影响,因为没有返回赋值的操作
System.out.println(doga.getAddress());//JavaBase.Dog@1b6d3586,与上面相同,在方法中改变对象,对此处无影响
}
private static void changeDogAddress(Dog doga) {
System.out.println(doga.getAddress());//JavaBase.Dog@1b6d3586,在这里还与上面的地址相同,因为地址还没发生改变
doga = new Dog("dogc");//生成新的dog对象,
System.out.println(doga.getName());//dogc
System.out.println(doga.getAddress());//JavaBase.Dog@4554617c,在这里地址就已经发生了改变
}
private static void changeDogName(Dog doga) {
//在此方法中,修改对象的字段值会修改元对象字段值,因为此时引用的是同一个对象
doga.setName("dogb");
}
}
class Dog{
String name;
Dog(String name){
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress(){
return super.toString();
}
}
向下转型(隐式)
在Java中,是不允许隐式向下转型操作的,这样会使精度降低。
float与double
float的精度是低于double的,在Java的基础类型中了解到,float是32位,double是64位的,由于存在小于0的情况,所以两个浮点类型都含有一个符号位,1位负数,0位正数,忽略不计。剩余位数中,float有8位指数,23位尾数(小数点之后的位数),double有11位指数,52位尾数。
在挑选使用哪种类型存储时需要从存储与精度中做选择,如果精度重要,则选择double,如果存储更重要,则选择float。
声明
在声明float类型时,需要加上f后缀,来表明这是一个低精度类型,否则会报错(Incompatible types. Found: 'double', required: 'float')。
private static void floatAndDouble() {
//直接将1.1赋值给float是隐式向下转型,是不可取的。
float f = 1.1;//Incompatible types. Found: 'double', required: 'float'
float f = 1.1f;
double d = 1.1;
}
short 与int,int与long.
private static void shortAndInt() {
int i = 1;
short s = i;//Incompatible types. Found: 'int', required: 'short',类型不符合/不兼容
}
private static void intAndLong() {
long l = 1;
int i = l;//Incompatible types. Found: 'long', required: 'int',
}
逻辑运算符
||,&&为逻辑运算符,
&&(与):如果两操作数中含有一个假则为假,
||(或):如果两操作数中含一个真则为真,
运算符
&、|、^、~、>>、<<、<<<、>>>为位运算符,其中三个左大于,右大于是无符号运算。
&:按位与操作,有0则为0,
| 0&0 | 0 |
|---|---|
| 0&1 | 0 |
| 1&0 | 0 |
| 1&1 | 1 |
|:按位或操作,有1则为1
| 0|0 | 0 |
|---|---|
| 0|1 | 1 |
| 1|0 | 1 |
| 1|1 | 1 |
Switch
从Java7开始,Switch支持在判断语句使用String类型,不支持long,float,double,当然如果条件过于复杂还是弃暗(switch)投明(if)比较好。
private static void switchAfterJava7() {
String s1 = "1";
switch (s1){
case "1":
System.out.println(s1);
case "2":
System.out.println(s1);
}
}
关键字
比较重要且常见的关键字有final,static,
Java中所有的关键字:
| 关键字 | 含义 |
|---|---|
| abstract | 表明类或者成员方法具有抽象属性 |
| assert | 断言,用来进行程序调试 |
| boolean | 基本数据类型之一,声明布尔类型的关键字 |
| break | 提前跳出一个块 |
| byte | 基本数据类型之一,字节类型 |
| case | 用在switch语句之中,表示其中的一个分支 |
| catch | 用在异常处理中,用来捕捉异常 |
| char | 基本数据类型之一,字符类型 |
| class | 声明一个类 |
| const | 保留关键字,没有具体含义 |
| continue | 回到一个块的开始处 |
| default | 默认,例如,用在switch语句中,表明一个默认的分支。Java8 中也作用于声明接口函数的默认实现 |
| do | 用在do-while循环结构中 |
| double | 基本数据类型之一,双精度浮点数类型 |
| else | 用在条件语句中,表明当条件不成立时的分支 |
| enum | 枚举 |
| extends | 表明一个类型是另一个类型的子类型。对于类,可以是另一个类或者抽象类;对于接口,可以是另一个接口 |
| final | 用来说明最终属性,表明一个类不能派生出子类,或者成员方法不能被覆盖,或者成员域的值不能被改变,用来定义常量 |
| finally | 用于处理异常情况,用来声明一个基本肯定会被执行到的语句块 |
| float | 基本数据类型之一,单精度浮点数类型 |
| for | 一种循环结构的引导词 |
| goto | 保留关键字,没有具体含义 |
| if | 条件语句的引导词 |
| implements | 表明一个类实现了给定的接口 |
| import | 表明要访问指定的类或包 |
| instance of | 用来测试一个对象是否是指定类型的实例对象 |
| int | 基本数据类型之一,整数类型 |
| interface | 接口 |
| long | 基本数据类型之一,长整数类型 |
| native | 用来声明一个方法是由与计算机相关的语言(如C/C++/FORTRAN语言)实现的 |
| new | 用来创建新实例对象 |
| package | 包 |
| private | 一种访问控制方式:私用模式 |
| protected | 一种访问控制方式:保护模式 |
| public | 一种访问控制方式:共用模式 |
| return | 从成员方法中返回数据 |
| short | 基本数据类型之一,短整数类型 |
| static | 表明具有静态属性 |
| strictfp | 用来声明FP_strict(单精度或双精度浮点数)表达式遵循[IEEE 754](baike.baidu.com/item/IEEE 754)算术规范 |
| super | 表明当前对象的父类型的引用或者父类型的构造方法 |
| switch | 分支语句结构的引导词 |
| synchronized | 表明一段代码需要同步执行 |
| this | 指向当前实例对象的引用 |
| throw | 抛出一个异常 |
| throws | 声明在当前定义的成员方法中所有需要抛出的异常 |
| transient | 声明不用序列化的成员域 |
| try | 尝试一个可能抛出异常的程序块 |
| void | 声明当前成员方法没有返回值 |
| volatile | 表明两个或者多个变量必须同步地发生变化 |
| while | 用在循环结构中 |
接下来说一下几个经常用到的,却又是大家(我)不了解的,搞不清楚,稀里糊涂的那些,面试时还经常会问到的。
final
final我们都知道的这是一个关键字,用来声明那些不会变的常量,其他的迷迷糊糊的就不太清楚了,可能有印象,但是具体到某一个方法,或者类上的时候,就不了解了。这就是我写这篇博客的原因,巩固自己的基础。
① 数据
声明的数据为常量,可以是编译时常量,也可以是在运行时被初始化后不能被改变的常量。
1)对于基本类型,final使数值不变;
2)对于引用类型,final使引用不变,就是引用其他对象,但是被引用的对象本身可以修改。
private static void finalOnData() {
//基本类型
final int num1 = 10;
// num1 = 2;//Cannot assign a value to final variable 'num1',意思是不能给一个final变量分配值
//引用类型
final Dog dog = new Dog("doga");
System.out.println(dog.getName());//doga
dog.setName("dogb");
System.out.println(dog.getName());//dogb
// dog = new Dog("dagc");//Cannot assign a value to final variable 'dog'
//可以看到,对于基本类型,不能改变基本类型变量的值,对于引用类型,不能引用其他对象
}
② 方法
声明的方法不能被子类重写。private声明的函数被隐式的声明为final类型,因此private类也是不可重写的。当子类中有与父类中的private方法名相同的方法时,这是子类自己的私有方法,与父类私有方法无关,private不像final关键字一样,final声明的方法不可以在子类中存在同名方法,名称相同就不可以,而private运行存在相同的方法名。
class Dog{
String name;
Dog(String name){
this.name = name;
}
final void finalDog(){
System.out.println("这是final方法,不能被重写!");
}
}
class DogA extends Dog{
DogA(String name) {
super(name);
}
void finalDog() {
//'finalDog()' cannot override 'finalDog()' in 'JavaBase.Dog'; overridden method is final
}
}
③ 类
最具代表的final类便是基础类型的封装类,如String类,被final声明的类不可以被继承。
public class FinalType extends String
//Cannot inherit from final 'java.lang.String'
//不能继承final String
public final class String
static
① 静态变量
-
静态变量:又称为类变量,也就是说这个变量是属于类的,类的所有实例都共享变量,所以在内存中只存在一份静态变量,可以直接通过类名来访问,也可以通过实例对象来访问(不推荐)。静态变量属于静态存储方式,静态变量不代表值不改变,她的值是可以改变的,但是改了之后就不能回到之前的初始值了。
JVM只会为静态变量分配一次内存空间,在类加载的时候完成静态变量的内存加载。
静态存储:在程序运行期间分配固定的存储空间。静态存储不一定是静态变量,但是静态变量是静态存储。静态存储需要加上static才能成为静态变量。
-
实例变量:每创建一个实例就会产生一个实例变量,他于该实例同生共死。
/**
* @author masuo
* @create 2021/7/10 9:36
* @Description static 的使用
*/
public class StaticType {
private static int s;
public static void main(String[] args) {
staticOnData();
}
/**
* static对数据的使用
* 1、静态变量
* 2、实例变量
*/
private static void staticOnData() {
//在声明的地方打个断点,在没有执行这一步之前,就已经存在static变量了,
// 即在调用函数之前static变量就已经存在了,说明静态变量在类加载的时候就存在了,
StaticClassOne sco = new StaticClassOne();
//在执行完声明之后,实例变量x被初始化,
// 在这里,0是系统默认赋值的,
int x = sco.x;//0
int y = StaticClassOne.y;
System.out.println("没有改变之前的StaticClassOne.y的值为:"+y);
//改变静态变量
for (int i = 0; i < 10; i++) {
StaticClassOne.y = StaticClassOne.y+i;
System.out.println("改变之后的StaticClassOne.y值为:"+StaticClassOne.y);
}
}
}
class StaticClassOne{
public int x; //实例变量
public static int y = 1; //静态变量
}
② 静态方法
同静态变量一样,静态方法在类加载的时候就已经存在了,在类加载的时候就存在的都不会依赖实例对象。所以静态方法必须是完整的,不依赖实例对象的存在,要有自己的实现,不能是抽象方法。(抽象与接口的区别在下面会讲到)。
静态方法只能访问该类的静态变量和静态方法。
方法中不能含有this和super等关键字,因为这都是和实体类相关联的。
/**
* @author masuo
* @create 2021/7/10 16:09
* @Description 静态方法测试
*/
public abstract class StaticClassFun {
public abstract static void get();//Illegal combination of modifiers: 'abstract' and 'static',不合法的修饰符组合
public abstract void getNO();//正确
}
//访问静态变量和访问静态方法
public static void get(){
System.out.println(y);//Non-static field 'y' cannot be referenced from a static context,非静态变量不可被静态方法引用
System.out.println(x);
get();
getX();
getAll();//Non-static method 'getAll()' cannot be referenced from a static context,非静态方法不可被静态方法引用
}
③ 静态语句块
静态语句块,在类初始化时运行一次,根据Java的类加载机制,类只在需要时被加载,且一次运行只加载一次,在初始化之后,类就存在某个地方(现在还不知道啊,还有好多要学啊,路还很长,只知道她大概是在JVM的内存中,具体是运行时方法区内存区,还是堆【不太可能】或者是别的地方,对于这一块了解的是真的很少。)
/**
* @author masuo
* @create 2021/7/10 16:09
* @Description 静态方法测试
*/
public abstract class StaticClassFun {
static {
System.out.println("这是静态语句块,你只会看到我出现一次哦!");
}
public static void main(String[] args) {
//在这里我们声明两个StaticClassOne类的实例对象,
StaticClassOne sco1 = new StaticClassOne();
StaticClassOne sco2 = new StaticClassOne();
//至于他只会加载一次的原因,看完以上两个静态变量以及静态方法的描述之后,多少应该会有一点自己的理解吧,在这里类只会加载一次,第二次声明的时候,因为类的pool中已经含有这个类的引用了,所以不会再对这个类进行初始化。
}
}
④ 静态内部类
同以上静态变量,静态方法一样,静态内部类是不依赖于外部类的,而非静态内部类是需要依赖于外部类的实例对象的,非静态内部类需要先创建实例对象才能创建非静态内部类。
/**
* @author masuo
* @create 2021/7/11 12:58
* @Description 静态内部类与非静态内部类
*/
public class StaticOuterClass {
class NonStaticInnerClass{
}
static class StaticInnerClass{
//静态内部类不能访问外部类的非静态变量和方法。
}
public static void main(String[] args) {
//非静态内部类不能单独声明
//NonStaticInnerClass nsic = new NonStaticInnerClass();//'JavaBase.StaticOuterClass.this' cannot be referenced from a static context,该类不能从静态上下文引用
//静态内部类可以不依赖实例对象直接声明
StaticInnerClass sic = new StaticInnerClass();
//声明外部类的实例对象
StaticOuterClass soc = new StaticOuterClass();
//利用外部类的实例对象声明非静态内部类
NonStaticInnerClass nsic = soc.new NonStaticInnerClass();
}
}
⑤ 静态导包
静态导包的作用是简化代码,具体参考:blog.csdn.net/u012338954/…
⑥ 初始化顺序
到了最重要的地方,这里一定要认真仔细的看,去理解,上面已经讲得说的很细了(在我看来)。
首先,静态变量具有很高的优先级,静态变量与静态语句块取决于他们在代码中的顺序。
在这里需要提一下一个经典的问题,Java中类加载的问题!!!
例:引用于:blog.csdn.net/zfx2013/art…
/**
* @author masuo
* @create 2021/7/11 22:34
* @Description java类的初始化循序
*/
public class JavaClassInitSequence {
/** 输出顺序为
* 1.父类静态(变量或者代码块)显式赋值,
* 2.子类静态(变量或者代码块)显式赋值,
* 3.父类非静态显式赋值(变量或代码块)
* 4.父类构造函数
* 5.子类非静态显式赋值(变量或代码块)
* 6.子类构造函数
*
* ***注意这里是显式赋值,首先必须是赋值语句,如果没有赋值语句,只是单纯的声明变量/或初始化变量,则不会输出,看注释1
*/
public static void main(String[] args) {
Cat d = new Cat();
//①父类静态fm,fa.②子类静态ss,sm.③父类非静态ft,fn,a.④子类非静态sa,st,sd
System.out.println("000");
Cat o = new Cat();
//
}
}
class Animal{
private int i = test();
private static int j = method();
//注释1:下面两行代码不会输出,因为他们不是显式赋值语句,隐式赋值即implicit(x)
private static int x;
static {
System.out.println("fa");
}
Animal(){
System.out.println("a");
}
{
System.out.println("fn");
}
private int test() {
System.out.println("ft");
return 1;
}
private static int method() {
System.out.println("fm");
return 1;
}
private int implicit(int i) {
System.out.println("ft");
return i;
}
}
class Cat extends Animal{
{
System.out.println("sa");
}
private int i = test();
static {
System.out.println("ss");
}
private static int j = method();
Cat(){
System.out.println("sd");
}
public int test(){
System.out.println("st");
return 1;
}
public static int method(){
System.out.println("sm");
return 1;
}
}
输出顺序为 1.父类静态(变量或者代码块)显式赋值, 2.子类静态(变量或者代码块)显式赋值, 3.父类非静态显式赋值(变量或代码块) 4.父类构造函数 5.子类非静态显式赋值(变量或代码块) 6.子类构造函数
instanceof
参考:www.cnblogs.com/ysocean/p/8…
boolean result = obj instanceof Class
概述:根据定义来看,可以说instanceof是一个Java的双目运算符,双目运算符就是需要两个变量才能运算的运算符,可以想象为==,用来测试左侧对象是否是右侧类的实例。
List<String> ls = new ArrayList<>();
System.out.println(ls.getClass());//class java.util.ArrayList
System.out.println(ls instanceof Collection);//true
//在这里,ls是ArrayList类,ArrayList是List的接口实现类,List是Collection的子类,
但是使用instanceof有以下几点需要注意:
- obj必须为引用类型,不能是基础类型
//基础类型
char c = '1';
System.out.println(c instanceof String);
//Inconvertible types; cannot cast 'char' to 'java.lang.String'
- NULL
NULL是一个特殊的类型,可以是任意引用类型的特殊符号,可能是因为所有类都有空值,所以所有类型都有null。
//null
System.out.println(null instanceof Object);//false
- obj为class类的实例对象
//class类的实例对象
String s = "123";
System.out.println(s instanceof String);//true
- obj为class接口的实现类
//接口实现类,如ArrayList是AbstractList的子类,是List等的接口实现类
List<String> ls2 = new ArrayList<>();
System.out.println(ls2 instanceof List);//true
- obj为class的直接或间接子类
看过第一个实例的话,我觉得你应该有自己的答案了。
//直接或间接子类
List<String> ls3 = new ArrayList<>();
System.out.println(ls3 instanceof AbstractCollection);//true
//在这里ls3是ArrayList类,ArrayList类是AbstractList类的子类,AbstractList类是AbstractCollection类的子类。
native
首先,native多用在方法上。
native方法就是Java调用非Java代码的接口,所以你见到的Java中含native关键字的方法大多都没有方法体,因为他们都是通过外接代码实现的。而使用native的主要原因是因为Java无法对操作系统底层进行操作,但是可以同过JNI(Java native interface)调用其他语言来实现底层访问。
构造方法
构造方法名与类名必须保持完全一致,包括类型及变量。
构造方法不含类型
class Test(int i){
public Test(int i){
//构造方法在new一个新的对象时会自动被调用
}
}