编程基础

时间:2019-03-15
本文章向大家介绍编程基础,主要包括编程基础使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

编程基础

常量

常量:在程序运行期间,固定不变的量。

常量的分类:

  1. 字符串常量:凡是用双引号引起来的部分,叫做字符串常量。例如:"abc"、"Hello"、"123"
  2. 整数常量:直接写上的数字,没有小数点。例如:100、200、0、-250
  3. 浮点数常量:直接写上的数字,有小数点。例如:2.5、-3.14、0.0
  4. 字符常量:凡是用单引号引起来的单个字符,就做字符常量。例如:'A'、'b'、'9'、'中'、'!','@'
  5. 布尔常量:只有量中取值。true、false。
  6. 空常量:null。代表没有任何数据。

举例:

//字符串常量
System.out.println(""); // 正确!字符串两个双引号中间的内容为空,像空一行的效果。

//字符常量
System.out.println(''); // 错误!两个单引号中间必须有且仅有一个字符,没有不行。

System.out.println('AB'); // 错误!两个单引号中间必须有且仅有一个字符,有两个不行。

//空常量。空常量不能直接用来打印输出。
System.out.println(null); // 错误!

System.out.println里 \n: 换行
\t: tab

变量

变量:程序运行期间,内容可以发生改变的量。

创建一个变量并且使用的格式:
1、数据类型 变量名称; // 创建了一个变量
变量名称 = 数据值; // 赋值,将右边的数据值,赋值交给左边的变量
2、一步到位的格式:
数据类型 变量名称 = 数据值; // 在创建一个变量的同时,立刻放入指定的数据值

注意:右侧数值的范围不能超过左侧数据类型的取值范围

  int num1;
  num1 = 10;
  System.out.println(num1); // 10

  int num2 = 25;
  System.out.println(num2); // 25

  var1 = false;
  System.out.println(var1); // false
  // 将一个变量的数据内容,赋值交给另一个变量
  // 右侧的变量名称var1已经存在,里面装的是false布尔值
  // 将右侧变量里面的false值,向左交给var2变量进行存储
  boolean var2 = var1;
  System.out.println(var2); // false

使用变量的注意事项

  1. 如果创建多个变量,那么变量之间的名称不可以重复。
  2. 对于float和long类型来说,字母后缀F和L不要丢掉。
  3. 如果使用byte或者short类型的变量,那么右侧的数据值不能超过左侧类型的范围。
  4. 没有进行赋值的变量,不能直接使用;一定要赋值之后,才能使用。
  5. 变量使用不能超过作用域的范围。
    【作用域】:从定义变量的一行开始,一直到直接所属的大括号结束为止。
  6. 可以通过一个语句来创建多个变量,但是一般情况不推荐这么写,不方便注释。
  // 同时创建了三个全都是int类型的变量
  int a, b, c;
  // 各自分别赋值
  a = 10;
  b = 20;
  c = 30;

  // 同时创建三个int变量,并且同时各自赋值
  int x = 100, y = 200, z = 300;

数据类型

  • 基本数据类型:包括 整数、浮点数、字符、布尔

  • 引用数据类型:包括 类、数组、接口

基本数据类型

1 自动类型转换

将“数据范围小的类型”自动提升为“数据范围大的类型”
数据范围从小到大,与字节数不一定相关
比较对象: int double
注意!右侧的数值大小不能超过左侧的类型范围 // byte num4 = 40;

2 转换规则

byte / short / char --> int --> long --> float --> double

​ boolean类型不能发生数据类型转换!

​ byte / short / char 运算时,先提升为int再计算。

​ 对于byte/short/char三种类型来说,如果右侧赋值的数值没有超过范围,那么javac编译器将会自动隐含地为我们补上一个(byte)(short)(char)。

  1. 如果没有超过左侧的范围,编译器补上强转。
  2. 如果右侧超过了左侧范围,那么直接编译器报错。

【举例】

int --> byte,不是自动类型转换,没有超过范围
  
    编译器将会自动补上一个隐含的(byte)
  
byte num1 = /*(byte)*/ 30; // 右侧没有超过左侧的范围
//byte num2 = 128;          // 报错,右侧超过了左侧的范围
int --> char
  
char zifu = /*(char)*/ 65; // A

​ 在给变量进行赋值的时候,如果右侧的表达式当中全都是常量,没有任何变量,那么编译器javac将会直接将若干个常量表达式计算得到结果。

short result = 5 + 8; // 等号右边全都是常量,没有任何变量参与运算

编译之后,得到的.class字节码文件当中相当于【直接就是】:

short result = 13;

右侧的常量结果数值,没有超过左侧范围,所以正确。
这称为“编译器的常量优化”。

但是注意:一旦表达式当中有变量参与,那么就不能进行这种优化了。

short num1 = 10; // 正确写法,右侧没有超过左侧的范围

short + short --> int + int --> int

//short result = a + b; // 错误写法!左侧需要是int类型

正确写法:右侧不用变量,而是采用常量,而且只有两个常量,没有别人。

short result = 5 + 8;
short s = 1;

s+=1;

s+=1 就是 s = (short)(s+1)

3 四则运算

一旦运算中有不同类型的数据,结果是大范围的类型
被除数/除数 = 商…余数
对于整数表达式来说,整数/整数=商,结果仍为整数,只看商,不看余数
取模(取余数):整数 % 整数 = 余数,只有对整数的除法来说,取模才有意义
% 判断奇偶 如:5%2=1

4 强制类型转换

将 数据范围大的类型 自动提升为 数据范围小的类型
数据类型 变量名 = (数据类型)(被转数据值)

  • double(浮点)类型转成整数型,直接去掉小数点,可能造成数据损失精度

    ​int a = (int)1.5

  • int强制转换成short,砍掉2个字节,可能造成数据丢失

5 字符串

String 如:String str = 'Hello';
对于字符串String(首字母大写,并不是关键字)来说,加号代表字符串连接操作。
/String + int --> String
任何数据类型 + 字符串 = 字符串

优先级问题

​ 从左往右
​ String + int + int -->
​ String + int -->
​ String
破解之法:小括号优于一切

引用数据类型

数组、类、接口

关键字

特点:

  1. 完全小写的字母。
  2. 在增强版的记事本当中(例如Notepad++)有特殊颜色。

如:public class static void

static关键字

static 关键字,可以修饰变量、方法和代码块。在使用的过程中,其主要目的还是想在不创建对象的情况下,去调用方法。

静态方法/变量占用时间长,不容易被释放

static{} 后面加载数据库jdbc用的广泛

类变量

如果一个成员变量使用了static关键字,那么这个变量不再属于对象自己,而是属于所在的类。多个对象共享同一份数据。

当 static 修饰成员变量时,该变量称为类变量。该类的每个对象都共享同一个类变量的值。任何对象都可以更改该类变量的值,但也可以在不创建该类的对象的情况下对类变量进行操作。

定义格式:

static 数据类型 变量名;

举例:

static int numberID;

类方法(静态方法)

当 static 修饰成员方法时,该方法称为类方法 。静态方法在声明中有 static ,建议使用类名来调用,而不需要创建类的对象。调用方式非常简单。

定义格式:

修饰符 static 返回值类型 方法名(参数列表){
  //执行语句
}

举例:在Student类中定义静态方法

public static void showNum(){
  System.out.println("num:" + numberOfStudent);
}

静态方法调用的注意事项:

  • 静态方法可以直接访问类变量和静态方法。

  • 静态方法不能直接访问普通成员变量或成员方法。反之,成员方法可以直接访问类变量或静态方法。

  • 静态方法中,不能使用this关键字。

  • 静态方法只能访问静态成员。

调用格式:

//访问类变量
类名.类变量名;

//调用静态方法
类名.静态方法名(参数)

静态原理

static 修饰的内容:
是随着类的加载而加载的,且只加载一次。
存储于一块固定的内存区域(静态区),所以,可以直接被类名调用。
它优先于对象存在,所以,可以被所有对象共享。

静态代码块

定义在成员位置,使用static修饰的代码块{ }。
位置:类中方法外。
执行:随着类的加载而执行且只执行一次,优先于main方法和构造方法的执行。

格式:

public class ClassName{
  static{
    //执行语句
  }
}

作用:给类变量进行初始化赋值

final

不可改变。可以用于修饰类、方法和变量。

  • 类:被修饰的类,不能被继承。
  • 方法:被修饰的方法,不能被重写。
  • 变量:被修饰的变量,不能被重新赋值。

修饰类

final class 类名{

}

修饰方法

修饰符 final 返回值类型 方法名(参数列表){
    //方法体
}

修饰变量

1、局部变量——基本类型

基本类型的局部变量,被final修饰后,只能赋值1次,不能再更改。

2、局部变量——引用类型

引用类型的局部变量,被final修饰后,只能指向一个对象,地址不得再更改,但是不影响对象内部的成员变量值的变化。

3、成员变量

对于成员变量来说,如果使用final关键字修饰,那么这个变量也照样是不可变。

  1. 由于成员变量具有默认值,所以用了final之后必须手动赋值,不会再给默认值了。
  2. 对于final的成员变量,要么使用直接赋值,要么通过构造方法赋值。二者选其一。
  3. 必须保证类当中所有重载的构造方法,都最终会对final的成员变量进行赋值。

被final修饰的常量名称,一般都有书写规范,所有字母都大写。

标识符

自己定义的内容。比如类名、方法名、变量名等。

硬要求

  • 标识符可以包含 英文字母26个(区分大小写)、数字0-9、$、_

  • 标识符不能以数字开头。

  • 标识符不能是关键字

软建议

  • 类名(接口名)规范:首字母大写,后面每个单词首字母大写(大驼峰)

  • 方法名规范:首字母小写,后面每个单词首字母大写(小驼峰)

  • 变量名规范:全部小写

运算符

复合赋值运算符

赋值运算符分为:
基本赋值运算符:就是一个等号“=”,代表将右侧的数据交给左侧的变量。
int a = 30;
复合赋值运算符

符号 用法 解释
+= a += 3 相当于 a = a + 3
-= b -= 4 相当于 b = b - 4
*= c *= 5 相当于 c = c * 5
/= d /= 6 相当于 d = d / 6
%= e %= 7 相当于 e = e % 7

注意事项:

  1. 只有变量才能使用赋值运算符,常量不能进行赋值。
  2. 复合赋值运算符其中隐含了一个强制类型转换。

自减运算符-- 自增运算符++

基本含义:让一个变量涨一个数字1,或者让一个变量降一个数字1
使用格式:写在变量名称之前,或者写在变量名称之后。例如:++num,也可以num++
使用方式:

  1. 单独使用:不和其他任何操作混合,自己独立成为一个步骤。
  2. 混合使用:和其他操作混合,例如与赋值混合,或者与打印操作混合,等。

使用区别:

  1. 在单独使用的时候,前++和后++没有任何区别。即:++num;和num++;是完全一样的。
  2. 在混合的时候,有【重大区别】
    A. 如果是【前++】,那么变量【立刻马上+1】,然后拿着结果进行使用【先加后用】
    B. 如果是【后++】,那么首先使用变量本来的数值,【然后再让变量+1】【先用后加】

注意事项:
只有变量才能使用自增、自减运算符。常量不可发生改变,所以不能用。

比较运算符

大于: >
小于: <
大于等于: >=
小于等于: <=
相等: == 【两个等号连写才是相等,一个等号代表的是赋值】
不相等: !=

注意事项:

  1. 比较运算符的结果一定是一个boolean值,成立就是true,不成立就是false
  2. 如果进行多次判断,不能连着写。
    数学当中的写法,例如:1 < x < 3
    程序当中【不允许】这种写法。

逻辑运算符【与 或 非】

与(并且) && 全都是true,才是true;否则就是false
或(或者) || 至少一个是true,就是true;全都是false,才是false
非(取反) ! 本来是true,变成false;本来是false,变成true

与“&&”,或“||”,具有短路效果:如果根据左边已经可以判断得到最终结果,那么右边的代码将不再执行,从而节省一定的性能。

注意事项:

  1. 逻辑运算符只能用于boolean值。
  2. 与、或需要左右各自有一个boolean值,但是取反只要有唯一的一个boolean值即可。
  3. 与、或两种运算符,如果有多个条件,可以连续写。
    两个条件:条件A && 条件B
    多个条件:条件A && 条件B && 条件C

TIPS:
对于1 < x < 3的情况,应该拆成两个部分,然后使用与运算符连接起来:
int x = 2;
1 < x && x < 3

三元运算符(推荐)

一元运算符:只需要一个数据就可以进行操作的运算符。例如:取反!、自增++、自减--
二元运算符:需要两个数据才可以进行操作的运算符。例如:加法+、赋值=
三元运算符:需要三个数据才可以进行操作的运算符。

格式:
数据类型 变量名称 = 条件判断 ? 表达式A : 表达式B;

流程:
首先判断条件是否成立:
如果成立为true,那么将表达式A的值赋值给左侧的变量;
如果不成立为false,那么将表达式B的值赋值给左侧的变量;
二者选其一。

注意事项:

  1. 必须同时保证表达式A和表达式B都符合左侧数据类型的要求。
  2. 三元运算符的结果必须被使用。

如:int i = (1==2 ? 100 : 200);

System.out.println(i);//200

又如:System.out.println(3==4 ? 100.12 : 400); //400.0

方法

方法定义注意事项

定义一个方法的格式:

public static void 方法名称() {
 方法体
}

方法名称的命名规则和变量一样,使用小驼峰。
方法体:也就是大括号当中可以包含任意条语句。

注意事项:

  1. 方法定义的先后顺序无所谓。
  2. 方法的定义不能产生嵌套包含关系。必须定义在一个类中,其他方法外。
  3. 方法定义好了之后,不会执行的。如果要想执行,一定要进行方法的【调用】。

如何调用方法,格式:

方法名称();

不能用输出语句(System.out...)调用void方法,因为方法执行后没有结果,就打印不出任何内容。

定义方法的完整格式

修饰符 返回值类型 方法名称(参数类型 参数名称, … ){
    方法体;
    return 返回值;
}

参数类型可以是不同的
方法体里定义的变量或数组,都仅在方法里存在,出了方法就没有了

  • 修饰符:现阶段的固定写法:public static

  • 返回值类型:也就是方法最终产生的数据结果是什么类型

  • 方法名称:方法的名字、规则和变量一样,小驼峰

  • 参数类型:进入方法的数据是什么类型

  • 参数名称:进入方法的数据对应的变量名称

PS:参数如果有多个,使用逗号进行分隔

  • 方法体:方法需要做的事,若干行代码

  • return:两个作用,①停止当前方法,②将后面的返回值还给调用处

  • 返回值:也就是方法执行后最终产生的数据结果

注意:return后面的“返回值”,必须和方法名称前面的“返回值类型”保持对应。

方法的三种调用方式

  • 单独调用:方法名称(参数);

  • 打印调用:System.out.println(方法名称(参数));

  • 赋值调用:数据类型 变量名称 = 方法名称(参数);

写方法时的注释

/**
*         //方法描述
*@param   //变量说明
*@param   //变量说明
*@return  //返回值说明
**/

方法重载和下列因素 有关

  • 参数个数不同

  • 参数类型不同

  • 参数的多类型顺序不同

核心:确保重载的方法的参数列表都是唯一的,不能有重复,确保根据参数个数可以直接找到需要调用的唯一的方法

    byte a = 20;
    byte b = 20;
    getAdd(a,b);
    
    getAdd((short)20,(short)20);
    
    getAdd(20L,20L);

方法重载和下列因素 无关

  • 与参数的名称无关

  • 与方法的返回值类型无关

Debug

  • 先断点:(多)选中要单独测试的行(红点)

  • 鼠标定位在打算Debug行,再 在左侧右键Debug || shift + F9 || 上方爬虫图标

  • 按 F8 依次向下查看结果

蓝色的行是定位,需要F8后,才会显示

定位到调用方法的方法所在

  • 断点到调用方法所在行,Debug,再鼠标点击所调用方法

  • Run -> Step into || F7

如果是多行调用方法,只会找到第一行的方法

  • control + 单击方法

打开工具栏,会显示定位到哪一个class

View -> Toolbar

流程控制语句

if else判断语句

if(关系表达式){
  语句体;
}
if(关系表达式){
  语句体1;
}
else{
  语句体2;
}
if (判断条件1){
  执行语句1;
} else if (判断条件2){
   执行语句2;
}
 ... 
} else if (判断条件n){
  执行语句n;     
} else {
  执行语句n+1;    
}

switch选择语句

swith(表达式,即被检测量){  
  case 常量值1:
    语句体1;
    break;
  case 常量值2:
    语句体2;
    break;
…
  default:
    语句体n+1;
    break;
} 

注意:

  1. 小括号里只能是 基本数据类型:byte/short/char/int 或 引用数据类型:String字符串、enum枚举
  2. 匹配哪一个case就从哪一个位置向下执行,直到遇到了break或者整体结束为止。
  3. 如果case的后面不写break,将出现穿透现象,程序会一直向后走,不会再判断case,直到遇到break,或者整体switch结束。
  4. 如果所有的case都和表达式的值不匹配,就会执行default语句体部分,然后程序结束掉

for循环语句

注意:

  1. 初始化表达式 不要忘记定义变量 如:int x = 1
  2. 步进表达式可以不写
  3. for的初始表达式如果想初始化多个变量,应该这样写: int a = 0,b = o,c = 0;

while循环语句

do while循环语句

for 和 while 的小区别

  • 控制条件语句所控制的那个变量,在for循环结束后,就不能再被访问到了,而while循环结束还可以继续使用,如果你想继续使用,就用while,否则推荐使用for。原因是for循环结束,该变量就从内存中消失,能够提高内存的使用效率。

  • 在已知循环次数的时候使用推荐使用for,循环次数未知的时推荐使用while。

跳出语句break

使用场景:终止switch或者循环 打断整个循环(if,但switch或for打断1个)

  • 在选择结构switch语句中

  • 在循环语句中

离开使用场景的存在是没有意义的

跳出语句continue

使用场景:结束本次循环,继续下一次的循环

死循环

死循环:也就是循环中的条件永远为true,死循环的是永不结束的循环。
例如:

while(true){
    循环体;
}

​ 在后期的开发中,会出现使用死循环的场景,例如:我们需要读取用户输入的输入,但是用户输入多少数据我们并 不清楚,也只能使用死循环,当用户不想输入数据了,就可以结束循环了,如何去结束一个死循环呢,就需要使用 到跳出语句了 if … break

死循环后面除了有break,不然轮不到!就报错!

小黑框里停止刷屏:control + C

循环结构的四个组成部分(面试)

  1. 初始化语句:在循环开始最初执行,而且只做唯一一次。
  2. 条件判断:如果成立,则循环继续;如果不成立,则循环退出。
  3. 循环体:重复要做的事情内容,若干行语句。
  4. 步进语句:每次循环之后都要进行的扫尾工作,每次循环结束之后都要执行一次。

数组

数组就是存储数据长度固定的容器,保证多个数据的数据类型要一致

特性:
数组是引用数据类型;
数组里面存储的数据类型都是一致的;
数组的长度在运行期间无法改变

定义数组

数组存储的数据类型[] 数组名字 = new 数组存储的数据类型[长度];

注意:数组有定长特性,长度一旦指定,不可更改。

int[] arr = new int[3];

动态初始化(指定长度)

// 动态初始化可以拆分成为两个步骤
int[] arr;
arr = new int[3];

数据类型[] 数组名 = new 数据类型[]{元素1,元素2,元素3...};

int[] arr = new int[]{1,2,3,4,5};

静态初始化(指定内容)

// 静态初始化的标准格式,可以拆分成为两个步骤
int[] arr;
arr = new int[]{1,2,3,4,5};

数据类型[] 数组名 = {元素1,元素2,元素3...};

int[] arr = {1,2,3,4,5};

省略型静态初始化(指定内容)
// 静态初始化的省略格式,不能拆分成为两个步骤。

方法中的变量arr保存的是数组的地址哈希值,因此是引用数据类型
数组中元素默认值0

索引

每一个存储到数组的元素,都会自动的拥有一个编号,从0开始,这个自动编号称为数组索引 (index),可以通过数组的索引访问到数组中的元素。
数组名[索引]

语法 功能
数组名.length 数组的长度
数组名.length - 1 数组的最大索引值
数组名[索引]=数值 为数组中的元素赋值
变量=数组名[索引] 获取出数组中的元素

JVM的内存划分

两个变量指向一个数组

//定义数组变量arr2,将arr的地址赋值给arr2
int[] arr2 = arr; 
arr2[1] = 9;         //arr[1]也会同时改变为9,因为同一个地址

数组空指针异常

arr = null 这行代码,意味着变量arr将不会在保存数组的内存地址,也就不允许再操作数组了,因此运行的时候 会抛出 NullPointerException 空指针异常。在开发中,数组的越界异常是不能出现的,一旦出现了,就必须要修 改我们编写的代码。

数组越界异常

我们不能访问数组中不存在的索引,程序运行后,将会抛出ArrayIndexOutOfBoundsException 数组越界异常。

数组遍历

public class Demo1 {
    public static void main(String[] args) {
        int[] arr = {1,2,3,4,5};
        for (int i = 0; i < arr.length; i++) {     //arr.fori  倒:arr.forr
            System.out.println(arr[i]);
        }
    }
}

数组作为方法的返回值, 返回的是数组的内存地址

public static void main(String[] args){
    //调用方法,接收数组的返回值
    //接收到的是数组的内存地址
    int[] arr = getArray();
    for(int i = 0; i < arr.length; i++) {
        System.out.println(arr[i]);
   }
}

public static int[] getArray() {
    int[] arr = {1,2,3,4,5};
   //返回数组的地址,返回到调用者
   return arr; 
}

数组作为方法参数

public static void main(String[] args){
    int[] arr = {1,2,3,4,5};
    change();
}

public static void change(int[] arr) {
    arr[0] = 200;    
}

方法的参数为基本类型时,传递的是数据值.
方法的参数为引用类型时,传递的是地址值.

设置方法的注释

  • File -> Setting -> Live Templates -> 先 + Templates Group,再 + Live Templates,在Abbreviation中輸入一個(這裏是)(在方法内輸入 ,再按 tab 就會跳出的意思)

  • 在Template text裏輸入以下:

 /**
 * @author: $user$
 $params$
 * @return $returns$
 * @description:
 * @date: $time$ $date$
 **/
  • 在Edit variables裏定義以下:

  • params - Expression:

groovyScript("def result=''; def params="${_1}".replaceAll('[\\[|\\]|\\s]', '').split(',').toList(); for(i = 0; i < params.size(); i++) {result+=' * @param ' + params[i] + ((i < params.size() - 1) ? '\n\b' : '')}; return result", methodParameters())

  • returns:methodReturnType()

  • time:time()

  • date:date()

  • Apply -> OK

通过键盘录入数字

Scanner

Scanner sc = new Scanner(System.in);
int j = sc.nextInt();

权限修饰符

public:公共的
protected:受保护的
default:默认的
private:私有的

不同权限的访问能力

建议

成员变量使用private,隐藏细节
构造方法使用public,方便构建对象
成员方法使用public,方便调用方法

内部类

将一个类A定义在另一个类B里面,里面那个类A就称为内部类,B则称为外部类

成员内部类

定义在类中方法外的类

class 外部类{
    class 内部类{

    }
}

访问特点【内用外,随意访问;外用内,需要内部类对象。】

内部类可以直接访问外部类的成员,包括私有成员。
外部类要访问内部类的成员,必须要建立内部类的对象。
创建内部类对象格式:

外部类名.内部类名 对象名 = new 外部类型().new 内部类型();

内部类仍然是一个独立的类,在编译之后会内部类会被编译成独立的.class文件,但是前面冠以外部类的类名$符号。
比如,Person$Heart.class

调用不同位置的变量

// 如果出现了重名现象,那么格式是:外部类名称.this.外部类成员变量名
public class Outer {
    int num = 10; // 外部类的成员变量
    public class Inner /*extends Object*/ {
        int num = 20; // 内部类的成员变量
        public void methodInner() {
            int num = 30; // 内部类方法的局部变量
            System.out.println(num); // 局部变量,就近原则
            System.out.println(this.num); // 内部类的成员变量
            System.out.println(Outer.this.num); // 外部类的成员变量
        }
    }
}

局部内部类

修饰符 class 外部类名称 {
    修饰符 返回值类型 外部类方法名称(参数列表) {
        class 局部内部类名称 {
            // ...
        }
    }
}

局部内部类,如果希望访问所在方法的局部变量,那么这个局部变量必须是【有效final的】。

备注:从Java 8+开始,只要局部变量事实不变,那么final关键字可以省略。

原因:

  1. new出来的对象在堆内存当中。
  2. 局部变量是跟着方法走的,在栈内存当中。
  3. 方法运行结束之后,立刻出栈,局部变量就会立刻消失。
  4. 但是new出来的对象会在堆当中持续存在,直到垃圾回收消失。
public class MyOuter {
    public void methodOuter() {
        int num = 10; // 所在方法的局部变量
        class MyInner {
            public void methodInner() {
                System.out.println(num);
            }
        }
    }
}

小节一下类的权限修饰符

public > protected > (default) > private
定义一个类的时候,权限修饰符规则:

  1. 外部类:public / (default)
  2. 成员内部类:public / protected / (default) / private
  3. 局部内部类:什么都不能写

匿名内部类【重点】

是内部类的简化写法。它的本质是一个【带具体实现的】【父类或者父接口】【匿名的】子类对象

如果接口的实现类(或者是父类的子类)只需要使用唯一的一次,
那么这种情况下就可以省略掉该类的定义,而改为使用【匿名内部类】。

匿名内部类的定义格式

接口名称 对象名 = new 接口名称() {
    // 覆盖重写所有抽象方法
};

对格式“new 接口名称() {...}”进行解析:

  1. new代表创建对象的动作
  2. 接口名称就是匿名内部类需要实现哪个接口
  3. {...}这才是匿名内部类的内容

引用类型用法总结

场景 说明
class作为成员变量 类作为成员变量时,对它进行赋值的操作,实际上,是赋给它该类的一个对象
interface作为成员变量 接口作为成员变量,对它进行赋值的操作,实际上是赋给它该接口的一个子类对象
interface作为方法参数和返回值类型 当接口作为方法的返回值类型时,返回的是它的子类对象。