1.运行Java程序需要使用JRE
2.开发Java程序需要使用JDK
该路径下存放了JDK的各种工具命令
该路径下存放了一些平台特定的头文件
该路径下存放了JDK工具的一些补充JAR包
在1.8环境下没有,之前的版本有这个文件
该路径下存放了JDK的相关配置文件
source源码目录
快捷键’Ctrl+R’在运行窗口键入’cmd’在弹出来的命令提示符窗口中输入’java -version’回车后出现以下内容即配置成功
dir
:查看当前路径下的内容cd 目录
:进入单级目录cd..
:回退到上一级目录cd 目录1/目录2
:进入多级目录cd /
:回退到盘符目录cls
:清屏exit
:退出命令提示符窗口ideaIU-2020.1.1.exe
IDEA安装教程(多图预警)
IntelliJ IDEA 下载安装配置教程(完整版)
十大Intellij IDEA快捷键
Idea快捷键大全(Windows)
史上最全的IDEA快捷键总结
没用过这些 IDEA 插件?怪不得写代码头疼
IntelliJ Idea 常用12款插件(提高开发效率),附优秀主题插件
值得推荐的Idea十几大优秀插件
晚上不用加班了,推荐十款精选IntelliJIdea插件,效率提升N倍
public class HelloWorld{
public static void main(String[] args){
System.out.println("Hello World!");
}
}
在命令行窗口输入javac HelloWorld.java
编译成HelloWorld.class字节码文件,然后再用java HelloWorld
运行java字节码文件
第一行:public表示权限公共的意思,外部可以随意调用和访问,class表示类,HelloWorld就是类的名字,此行代码表示的含义:创建了一个名为HelloWorld的权限公开的Java类
第二行:main叫做方法名(或者叫做函数名),static表示
的,也就是只有一份,void表示无返回值,此行代码的含义:main方法是属于当前类HelloWorld的,只要这一份,且执行后没有值返回
第三行:输出语句,系统调用内部输出方法,打印出双引号中的内容
以双斜杠//
标识,只能注释一行内容,用在注释信息内容少的地方
包含在/*和*/
的之间,为了可读性比较好,一般首行和尾行不写注释信息(美观)
包含在/** 和 */
之间,也能注释多行内容,一般用在类、方法和变量上面,用来描述其作用。注释后,鼠标放在类和变量上面会自动显示出来我们注释的内容
所有关键字都是小写的
boolean、int、long、short、byte、float、double、char、enum、class、interface
if、else、do、while、for、switch、case、default、break、continue、return、try、catch、finally、assert
public、protected、private、final、void、static、strict、abstract、transient、synchronized、volatile、native
package、import、throw、throws、extends、implements、this、supper、instanceof、new
true、false、null
goto、const
_
、美元符号$
¥
以及所有在十六进制0xc0前的ASCII码组成常量是指在程序的整个运行过程中值保持不变的量
常量不同于常量值,它可以在程序中用符号来代替常量值使用,因此在使用前必须先定义。常量与变量类似也需要初始化,即在声明常量的同时要赋予一个初始值。常量一旦初始化就不可以被修改。
final int num = 10
类型 | 含义 | 数据举例 |
---|---|---|
整数常量 | 所有的整数 | 0,1,567 |
小数常量 | 所有的小数 | 0.0,2.55 |
字符常量 | 单引号引起来,只能写一个字符,必须有内容 | ‘a’,’ ',‘好’ |
字符串常量 | 双引号引起来,可以写多个字符,也可以不写 | “A”,“Hello” |
布尔常量 | 只有2个值 | true,false |
空常量 | 只有一个值 | null |
常量值又称为字面常量,它是通过数据直接表示的,因此有很多种数据类型,像整型和字符串型等
变量是指在程序的整个运行过程中值可以发生改变的量
Java是强类型语言:
变量的作用域规定了变量所能使用的范围,只有在作用域范围内变量才能被使用。根据变量声明地点的不同,变量的作用域也不同。
Java的成员变量有两种,分别是全局变量和静态变量(类变量)。定义在方法体和语句块之外不属于任何一个方法,作用域就是整个类。
全局变量是有默认值的,而局部变量没有默认值,所以使用前一定要进行初始化
名称 | 修饰 | 访问 | 生命周期 |
---|---|---|---|
全局变量(实例变量) | 无static修饰 | 对象名.变量名 | 只要对象被当作引用,实例变量就将存在 |
静态变量(类变量) | 用static修饰 | 变量名或类名.变量名或对象名.变量名 | 其生命周期取决于类的生命周期,类被垃圾回收机制彻底回收时才会被销毁 |
[admonition title=“注意” color=“red”]之间访问变量名需要在本类,最好的最规范的使用是类名.变量名[/admonition]
/*
*全局变量(实例变量):
* 作用范围:在类的内部,方法之外
* static关键字
* 修饰方法,则该方法是静态的,静态的就是表示只有一份,属于类,无论有多少个对象,都是对这一个进行修改
* 修饰变量(属性),则该变量是静态的,表示只有一份属于类
*/
public class GlobalVariable {
// 创建了一个成员变量age【全局变量(实例变量)、静态变量】
int age = 21; // 创建了一个全局变量
static int age1 = 19;
public static void main(String[] args){
// 使用全局变量age
// 访问方式:对象.全局变量
GlobalVariable gv = new GlobalVariable();
gv.age = 18;
System.out.println(gv.age);
// 静态变量的访问
// 访问方式:类名.变量名或对象名.变量名
GlobalVariable.age1 = 30;
System.out.println((GlobalVariable.age1));
}
}
局部变量是指在方法或者方法代码块中定义的变量,其作用域是其所在的代码块
/*
* 局部变量
* 变量的作用范围是部分,而非类的全部
*
*/
public class LocalVariable {
// main方法,有参数String数组,变量名是args,所有args的作用范围是整个main方法内容
public static void main(String[] args){
LocalVariable lv = new LocalVariable();
sum1();
}
// sum方法的参数中,变量a和变量b的作用范围是这个方法内部
public static void sum(int a, int b){
a = 0;
b = 1;
}
// sum方法的参数中,变量a和变量b的作用范围是这个方法内部
public static void sum1(){
int a = 1;
int b = 0;
System.out.println(a);
System.out.println(b);
}
// 面试题:代码块是属于对象还是属于类?
// 代码块是属于对象的,当创建对象,代码块开始运行
{
int a = 0;
int b = 1;
System.out.println(a);
System.out.println(b);
}
}
在整个方法内有效
从定义这个变量开始到方法结束这一段时间内有效
从定义这个变量开始到代码块结束这一段时间内有效
百度百科
计算机存储信息的最小单位,称之为位(bit),音译比特,二进制的一个"0"或一个"1"叫一位
字节(Byte)是一种计量单位,表示数据量多少,它是计算机信息技术用于计量存储容量的一种计量单位,8个二进制位组成1个字节。在ASCII码中,一个标准英文字母(不分大小写)占一个字节位置,一个标准汉字占2个字节位置。
字符是指计算机中使用的文字和符号
JDK:Java development kit Java开发工具:开发Java应用程序的最小环境
JRE:Java Runtime Environment Java运行环境:运行Java应用程序的最小环境
JVM:Java Vritral Meschines Java虚拟机:解析和执行字节码文件的
大小关系:JDK>JRE>JVM
名:JAVA_HOME 值:jdk在系统盘中的存储路径
名:path 值:%JAVA_HOME%\bin
在程序运行过程中不会发生改变的量
常量分类:
常量是由final所修饰的量
例如:final int A = 0;final static String NAME = "张三"
在程序运行的过程中可以发生改变的量
String info = "Hi,codewing23"; // 变量info声明并初始化
info = "你好,张三" // 对变量info进行修改
System.out.println(info);
定义在类里边,方法和代码块外部的变量
全局变量(实例变量)和静态变量(类变量:只有一份任何一个对象改变变量的值都会影响其他对象的访问)
定义在方法内部、定义在方法的参数中、定义在代码块中的变量
代码规范的必看手册
Java开发手册(嵩山版).pdf
Java语言支持的数据类型分为两种基本数据类型和引用数据类型
包含整数(四个)、浮点数(两个)、字符、布尔
类型名称 | 关键字 | 占用内存 | 取值范围 |
---|---|---|---|
字节型 | byte | 1字节 | -128~127 |
短整型 | short | 2字节 | -32768~32767 |
整型 | int | 4字节 | -2147483648~2147483647 |
长整型 | long | 8字节 | -9223372036854775808L~9223372036854775807L |
单精度浮点型 | float | 4字节 | +/-3.4E+38F(6~7个有效位) |
双精度浮点型 | double | 8字节 | +/-1.8E+308(15个有效位) |
字符型 | char | 2字节 | ISO单一字符集 0~65536 |
布尔型 | boolean | 1字节 | true或false |
char代表字符型,实际上字符型也是整数类型,相当于无符号整数
###类型默认值
数据类型 | 默认值 |
---|---|
byte | 0 |
short | 0 |
int | 0 |
long | 0L |
float | 0.0f |
double | 0.0d |
char | ‘u0000’ |
String(or any object) | null |
boolean | false |
整数默认int类型,浮点数默认double类型,想用float得加f(大小写均可)
包含类、数组、接口
整型、实型(常量)字符型数据可以混合运算。运算中,不同类型的数据先转化为同一类型,然后进行运算。
[alert color=“red”]注意[/alert]
int i = 128; byte b = (byte)i
(int)23.7 == 23; (int)-45.89f == -45
public class Demo {
public static void main(String[] args) {
int a = 3;
int b = (int)(a + 2.3); // 而在C++中直接int b = a + 2.3;
System.out.println(b);
}
}
运算符 | - |
---|---|
+ | 加法运算,字符串连接运算 |
- | 减法运算 |
* | 乘法运算 |
/ | 除法运算 |
% | 取模运算,两个数字相除取余数 |
++、– | 自增自减运算 |
操作符 | 描述 | 例子 |
---|---|---|
= | 简单的赋值运算符,将右操作数的值赋值给左侧操作数 | C=A+B将把A+B的值赋给C |
+= | 加和赋值操作符,它把左操作数和右操作数相加赋值给左操作数 | C+=A等价于C=C+A |
-= | 减和赋值操作符,它把左操作数和右操作数相减赋值给左操作数 | C-=A等价于C=C-A |
*= | 乘和赋值操作符,它把左操作数和右操作数相乘赋值给左操作数 | C*=A等价于C=C*A |
/= | 除和赋值操作符,它把左操作数和右操作数相除赋值给左操作数 | C/=A,C与A同类型时,等价于C=C/A |
(%) | 取模和赋值操作符,它把左操作数和右操作数取模后赋值给左操作数 | C%=A等价于C=C%A |
<<= | 左位移赋值运算符 | C<<2等价于C=C<<2 |
>>= | 右位移赋值运算符 | C>>2等价于C=C>>2 |
&= | 按位与赋值运算符 | C&=2等价于C=C&2 |
^= | 按位异或赋值操作符 | C=2等价于C=C2 |
|= | 按位或赋值操作符 | C|=2等价于C=C|2 |
public class Demo {
public static void main(String[] args) {
byte a = 20;
a += 10;
/*
a = byte + int
a = int + int
a = int;
a = (byte)int;
*/
System.out.println(a);
}
}
[admonition title=“注意” color=“red”]混合赋值运算中隐含了一个强制类型转换![/admonition]
运算符 | 描述 |
---|---|
== | 检查如果2个操作数的值是否相等,如果相等则条件为真 |
!= | 检查如果2个操作数的值是否相等,如果值不相等则条件为真 |
> | 检查左操作数的值是否大于右操作数的值,如果是那么条件为真 |
< | 检查左操作数的值是否小于右操作数的值,如果是那么条件为真 |
>= | 检查左操作数的值是否大于或等于右操作数的值,如果是那么条件为真 |
<= | 检查左操作数的值是否小于或等于右操作数的值,如果是那么条件为真 |
操作数 | 描述 |
---|---|
&& | 称为逻辑运算符。当且仅当2个操作数都为真,条件才为真 |
|| | 称为逻辑或操作符。如果任何2个操作数任何一个为真,条件为真。 |
! | 称为逻辑非运算符。用来反转操作数的逻辑状态。如果条件为true,则逻辑非运算符将得到false。 |
Java定义了位运算符,应用于整数类型(int)、长整型(long)、短整型(short)、字符型(char)、和字节型(byte)等类型。
定义:
int A = 60; int B = 13;
操作符 | 描述 | 例子 |
---|---|---|
& | 如果相对应位都是1,则结果为1,否则为0 | (A&B),得到12,即00001100 |
| | 如果相对应位都是0,则结果为0,否则为1 | (A|B)得到61,即00111101 |
^ | 如果相对应位值相同,则结果为0,否则为1 | (A^B)得到49,即00110001 |
~ |
按位取反运算符翻转操作数的每一位,即0变成1,1变成0 | (~A)得到-61,即11000011 |
<< | 按位左移运算符。左操作数按位左移右操作数指定的位数 | A<<2得到240,即11110000 |
>> | 按位右移运算符。左操作数按位右移右操作数指定的位数 | A>>2得到15,即1111 |
>>> | 按位右移补零操作符。左操作数的值按右操作数指定的位数右移,移动得到的空位以零填充 | A>>>2得到15,即00001111 |
60的二进制是00111100左移2后为0011110000
右移2后为00001111
二进制运算是以每4位运算,不足要在前面补0
条件运算符也被称为三元运算符,或者也叫三目运算符。
该运算符有3个表达式,格式:数据类型 变量名 = 布尔类型的表达式1 ? 表达式2 : 表达式3;
[alert color=“red”]注意[/alert]
类别 | 操作符 | 关联性 |
---|---|---|
后缀 | ()[].(点操作符) | -> |
一元 | a++,a– | -> |
一元 | ++a,–a,~,! | <- |
乘性 | */% | -> |
加性 | ± | -> |
移位 | >>,>>>,<< | -> |
关系 | >,>=,<,<= | -> |
相等 | ==,!= | -> |
按位与 | & | -> |
按位异或 | ^ | -> |
按位或 | | | -> |
逻辑与 | && | -> |
逻辑或 | || | -> |
条件 | ?: | <- |
赋值 | =,+=,-=,*=,/=,%=,>>=,<<=,&=,^=,|= | <- |
逗号 | , | -> |
import java.util.Scanner;
public class Demo {
public static void main(String[] args) {
// 创建一个具有输入功能的对象 API Scanner
Scanner scan = new Scanner(System.in);
System.out.println("请您输入商品的数量:");
int num = scan.nextInt();
System.out.println("请您输入商品的价格:");
double price = scan.nextDouble();
// 计算总价
double total = num * price;
System.out.println("商品的总价是:" +total);
// 付款
System.out.println("请您付款:");
double money = scan.nextDouble();
// 找零
double change = money - total;
System.out.println("找零:" + change);
}
}
import java.util.Scanner;
public class Demo {
public static void main(String[] args) {
// 判断年龄是否大于18
Scanner scan = new Scanner(System.in);
System.out.println("请输入您的年龄:");
int age = scan.nextInt();
int pre = 18;
if(age < pre) {
System.out.println("成年");
} else {
System.out.println("未成年");
}
}
}
import java.util.Scanner;
public class Demo {
// 从JDK7.0开始。switch-case可以支持字符串表达式
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
System.out.println("请输入值:");
int num = scan.nextInt();
switch(num) {
case 1:
System.out.println("1"); break;
case 2:
System.out.println("2"); break;
case 3:
System.out.println("3"); break;
default:
System.out.println("NULL"); break;
}
}
}
[alert color=“red”]注意[/alert]
import java.util.Scanner;
public class Demo {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
System.out.println("请输入值:");
int num = scan.nextInt();
int pre = 10, i = 0;
while(num < pre) {
i ++;
System.out.println("第" + i + "个值是:" + num);
num ++;
}
}
}
import java.util.Scanner;
public class Demo {
public static void main(String[] args) {
// 让数字倒序输出
Scanner scan = new Scanner(System.in);
System.out.println("请输入值:");
int num = scan.nextInt();
int pre = 0;
do {
pre = num % 10 + pre * 10;
num /= 10;
}while(num != 0);
System.out.println("倒序后前方无0的值为:" + pre);
}
}
import java.util.Scanner;
public class Demo {
public static void main(String[] args) {
// 求1-num的和
Scanner scan = new Scanner(System.in);
System.out.println("请输入值:");
int num = scan.nextInt();
int res = 0;
for(int i = 1; i <= num; i ++) {
res += i;
}
System.out.println("1-num的和为:" + res);
}
}
public class Demo {
public static void main(String[] args) {
int n = 10;
for(int i = 1; i < n; i ++) {
for(int j = 1; j <= i; j ++){
System.out.print(j + " * " + i + " = " + i * j + "\t");
}
System.out.println();
}
}
}
方法用于封装一个特定的功能,定义时需要考虑5个要素:修饰词、返回值类型、方法名、参数列表、方法体。
[alert color=“red”]注意[/alert]
[alert color=“blue”]标准格式[/alert]
/**
* 修饰词 返回值类型 方法名称(参数列表) {
* 方法体
* return 返回值
* }
* 修饰词:public static
* 返回值类型:方法执行完之后,要给出的结果类型【基本数据类型,引用数据类型】
* 方法名称:符合java标识符的规则。“见名知意”格式“小驼峰”命名法
*
*/
import java.util.Scanner;
public class Demo {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
System.out.print("请输入a:");
int a = scan.nextInt();
System.out.print("请输入b:");
int b = scan.nextInt();
int res = sum(a, b);
System.out.println("两数之和为:" + res);
}
public static int sum(int a, int b) {
return a + b;
}
}
方法名称(参数列表);
System.out.println(方法名称(参数列表))
数据类型 变量名称 = 方法名称(参数列表)
只有定义的方法有返回值得时候才可以使用赋值调用
/**
* 方法的重载:对于功能类似的方法而言,方法名称又太多,会导致不必要的麻烦,所以提出了方法重载的概念!
* 方法的重载:Overload
* 多个方法名称相同,参数列表不同!
* 注意:参数列表不同的体现
* 1.参数个数的不同
* 2.参数类型的不同
* 3.多个参数类型的顺序不同
*
* 不可以通过方法的返回值来判断方法是否发生重载【方法的重载和方法的返回值无关】
* 方法的重载和参数名称无关,只与参数列表相关
* 方法的重载只与参数列表相关,和返回值无关
*/
public class Demo {
public static void main(String[] args) {
System.out.println(sum(1, 3));
}
public static int sum(int num1, int num2) {
return num1 + num2;
}
public static double sum(double num1, int num2) {
return num1 + num2;
}
public static double sum(int num2, double num1) {
return num1 + num2;
}
}
public class Demo {
public static void main(String[] args) {
System.out.println(method(1L, 2L));
}
// 两个byte
public static boolean method(byte a, byte b) {
return a == b;
}
// 两个short
public static boolean method(short a, short b) {
if(a == b) {
return true;
}else {
return false;
}
}
// 两个int
public static boolean method(int a, int b) {
boolean res;
if(a == b) {
res = true;
}else {
res = false;
}
return res;
}
// 两个long
public static boolean method(long a, long b) {
boolean res = a == b ? true : false;
return res;
}
}
特点:
注意:左边的数据类型要和右边的数据类型一致
静态和动态数组的初始化可以分两步进行即先声明后赋值,而静态省略格式不可以
标准格式:数据类型[] 数组名 = new 数据类型[]{内容1, 内容2, 内容3,…};
public class Demo {
public static void main(String[] args) {
int[] arr = new int[]{10, 11, 12, 13};
String[] arr1 = new String[]{"Hello", "Java", "C++"};
System.out.println(arr1[1]);
// 省略格式
double arr2 = {0.1, 0.2, 0.3};
}
}
标准格式:数据类型 [] 数组名 = new 数据类型[大小]
public class Demo {
public static void main(String[] args) {
int[] arr = new int[100];
String[] arr1 = new String[10];
double[] arr2 = new double[20];
}
}
public class Demo {
public static void main(String[] args) {
int[] arr1 = new int[]{1, 3, 5};
int len = arr1.length;
System.out.println("arr1的长度:" + len);
System.out.println(arr1); //直接输出数组的名称得到的是数组内存的地址
System.out.println(arr1[2]);
}
}
public class Demo {
public static void main(String[] args) {
int arr[] = new int[3];
System.out.println(arr[3]); // ArrayIndexOutOfBoundsException:3下标越界
}
}
public class Demo {
public static void main(String[] args) {
int[] arr = null;
System.out.println(arr[0]); // NullPointerException空指针异常
}
}
public class Demo {
public static void main(String[] args) {
int n = 10;
int[] arr = new int[n];
for(int i = 0; i < n; i ++) {
arr[i] = i + 1;
}
for(int i = 0; i < n; i ++) {
System.out.println(arr[i]);
}
}
}
public class Demo {
public static void main(String[] args) {
int[] arr = {119, 21, 34, 45, 52};
int max = Integer.MIN_VALUE;
int min = Integer.MAX_VALUE;
for (int value : arr) {
if (value > max) {
max = value;
}
if (value < min) {
min = value;
}
}
System.out.println("数组中的最大值为:" + max);
System.out.println("数组中的最小值为:" + min);
}
}
Java虚拟机要运行程序,必须要对内存进行空间的分配和管理
区域名称 | 作用 |
---|---|
寄存器 | 给CPU使用,和我们开发无关 |
本地方法栈 | JVM在使用操作系统功能的时候使用,和我们开发无关 |
方法区 | 存储可以运行的class文件【包含成员方法的信息、成员变量的信息】 |
堆内存 | 存储对象或者数组,new来创建的,都存储在堆内存 |
栈内存 | 方法运行时使用的内存,比如main方法,进入方法栈中进行 |
[alert color=“red”]注意[/alert]
[alert color=“blue”]Java虚拟机运行时数据区[/alert]
Java内存分配全面浅析
public class Demo {
public static void main(String[] args) {
int[] arr = {1, 2, 3};
method(arr);
}
public static void method(int[] arr) {
System.out.println("接收到的数组是:" + arr);
}
}
三要素:
1. 返回值类型:int/int/int[]
2. 方法名称:getMax/getMin/method
3. 参数列表:int[] arr
import java.util.Arrays;
public class Demo {
public static void main(String[] args) {
int[] arr = {370, 440, 458, 1902};
int max = getMax(arr);
int min = getMin(arr);
int[] arr1 = method(arr);
System.out.println("数组中的最大值为:" + max);
System.out.println("数组中的最小值为:" + min);
// Arrays表示是数组的操作类,内部含有一个输出所有内容的方法toString
System.out.println(Arrays.toString(arr1));
}
// 方法1:求最大值
public static int getMax(int[] arr) {
int max = Integer.MIN_VALUE;
for(int value : arr) {
if(max < value) {
max = value;
}
}
return max;
}
// 方法2:求最小值
public static int getMin(int[] arr) {
int min = Integer.MAX_VALUE;
for(int value : arr) {
if(min > value) {
min = value;
}
}
return min;
}
// 方法3:最大值、最小值、平均值
public static int[] method(int[] arr) {
int max = getMax(arr);
int min = getMin(arr);
int res = 0;
for(int value : arr) {
res += value;
}
int avg = res / arr.length;
// int arr1 = {};
// return arr1;
// 要用之前的数组不能省略,要new一个对象
arr = new int[]{max, min, avg};
return arr;
}
}
数组的扩容
数组的长度一旦被声明固定,在运行期是不可以被改变
采用"倒手的动作",将原数组进行了复制,前提是新声明的数组长度要比原来的数组的长度至少大1
import java.util.Arrays;
public class Demo {
public static void main(String[] args) {
// 原数组
int[] arr1 = {1, 2, 3, 4, 5};
// 新数组
int[] arr2 = new int[arr1.length + 1];
System.out.println(Arrays.toString(arr1));
System.out.println(Arrays.toString(arr2));
System.out.println("=================");
for (int i = 0; i < arr1.length; i++) {
arr2[i] = arr1[i];
}
// 为最后一个元素进行赋值
arr2[arr2.length - 1] = 3;
System.out.println(Arrays.toString(arr2));
}
}
ctrl+alt+m
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1bo8huzk-1640435871934)(https://cdn.jsdelivr.net/gh/zhangying458/CDN/blog/Java-base/amgtv-nlovy.webp)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uCjz2zM4-1640435871934)(https://cdn.jsdelivr.net/gh/zhangying458/CDN/blog/Java-base/afbym-uk3h9.webp)]
import java.util.Arrays;
public class Demo {
public static void main(String[] args) {
// 原数组
int[] arr1 = {1, 2, 3, 4, 5};
method1(arr1);
}
public static void method1(int[] arr1) {
// 新数组
int[] arr2 = new int[arr1.length + 1];
System.out.println(Arrays.toString(arr1));
System.out.println(Arrays.toString(arr2));
System.out.println("=================");
for (int i = 0; i < arr1.length; i++) {
arr2[i] = arr1[i];
}
// 为最后一个元素进行赋值
arr2[arr2.length - 1] = 3;
System.out.println(Arrays.toString(arr2));
}
}
import java.util.Arrays;
/**
* public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
* 其中:
* .native 是一个修饰词,表示该方法是一个本地方法【跟jvm进行交互的方法】,使用了java的代码调用了非java的功能接口【了解】
* .src:原数组arr1
* .srcPos:原数组的起始位置 0
* .dest:目标数组arr2
* .destPos:目标数组的起始位置
* .length:要复制的长度
*/
public class Demo {
public static void main(String[] args) {
// 原数组
int[] arr1 = {1, 2, 3, 4, 5};
// 新数组
int[] arr2 = new int[arr1.length + 1];
System.arraycopy(arr1, 0, arr2, 0, arr1.length);
arr2[arr2.length - 1] = 3;
System.out.println("复制之后的数组:" + Arrays.toString(arr2));
}
}
import java.util.Arrays;
/**
* public static int[] copyOf(int[] original, int newLength) {
* int[] copy = new int[newLength];
* System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));
* return copy;
* }
* .original:表示是要被复制的数组arr1
* .newLength:新的长度【如果新的长度比原长度大,表示扩容;如果长度比原长度小,表示缩容】
*/
public class Demo {
public static void main(String[] args) {
// 原数组
int[] arr1 = {1, 2, 3, 4, 5};
// 新数组
// int[] arr2 = Arrays.copyOf(arr1, arr1.length + 1);
// System.out.println(Arrays.toString(arr2));
arr1 = Arrays.copyOf(arr1, arr1.length + 1); // 生成的新的数组,栈中arr1指向的地址发生变化
System.out.println(Arrays.toString(arr1));
}
}
面向对象编程(Object-oriented programming,简称OOP)是指以软件中的"对象个体"为思考方向的编程思想。这里的对象个体泛指现实中一切事物,每种事物都具备自己的属性和行为。面向对象思想就是在计算机程序设计过程中,参照现实中事物,将事物的属性特征、行为特征抽象出来,描述成计算机事件的设计思想。它区别于面向过程思想,强调是通过调用对象的行为来实现功能,而不是自己一步一步的去操作实现。
举例:
面向过程:买菜->洗菜->切菜->炒菜->吃菜
面向对象:餐馆->下单->吃菜
面向过程:衣服->放盆->放洗衣粉->加水->浸泡->揉搓->清洗衣服->拧干->晾
面向对象:衣服->打开全自动洗衣机->扔衣服->按钮->晾起来
[alert color=“blue”]核心思想[/alert]
复用、可以扩展
[alert color=“red”]三大特征[/alert]
是一组相关属性和行为的集合。可以看成是一类事物的模板,使用事物的属性特征和行为特征来描述该类事物。
现实中,描述一类事物:
属性:就是该事物的状态信息
行为:就是该事物能够做什么
举例:小猫
是一类事物的具体体现。对象是类的一个实例,必然具备该类事物的属性和行为
现实中,一类事物的一个实例:一只小猫
举例:一只小猫
public class Emp {
/**
* 属性:成员变量
*/
String name;
int age;
double salary;
int emp;
/**
* 方法:成员方法
*/
public void eat() {} // 吃
public void run() {} // 跑
public void work() {} // 工作
}
/**
* 类:
* 描述事物的一个集合,涵盖了事物的属性特征和行为特征
* 属性特征:是描述事物的状态(是什么)
* 行为特征:是描述事物的行为(能做什么)
* 对象:
* 是类的一个具体实例
* 关系:
* 类是抽象的,对象是具体的
* 类是对象的模板,对象是类的实例
*/
public class Demo {
public static void main(String[] args) {
// String name = "张三";
// int age = 21;
// double salary = 2.0e4;
// int emp = 12;
/*
对象的创建:就是实例化类的过程
数据类型:8种基本数据类型;引用数据类型
格式:
数据类型 变量名称 = new 数据类型();
*/
// 访问属性,通过创建好的对象的引用(变量名称),去打点访问成员属性
Emp emp1 = new Emp();
emp1.name = "李四";
emp1.age = 19;
emp1.salary = 1.0e4;
emp1.emp = 20;
println(emp1);
// 通过通过创建好的对象的引用(变量名)去打点访问成员方法
emp1.eat(emp1);
emp1.run(emp1);
emp1.work(emp1);
}
private static void println(Emp emp1) {
System.out.println(emp1.emp + "号员工" + emp1.name + "现在月薪" + emp1.salary + ",听说是搞Java的,才" + emp1.age + "岁就这么厉害!");
}
/**
* 专门用来打印输出员工信息
* @param emp 员工的工号
* @param age 员工的年龄
* @param name 员工的姓名
* @param salary 员工的薪水
*/
public static void println(int emp, int age, String name, double salary) {
System.out.println(emp + "号员工" + name + "现在月薪" + salary + ",听说是搞Java的,才" + age + "岁就这么厉害!");
}
}
创建对象:
new的作用:
使用对象访问类中的成员:
/**
* 创建了一个名为Student的类
*/
public class Student {
// 成员变量
String name; // 学生名称
int stuId; // 学生学号
int age;
// 成员方法
public void study() {
System.out.println("学习!");
}
public void run() {
System.out.println("跑步!");
}
public static void main(String[] args) {
// 创建Student类的对象
// 数据类型 对象名 = new 数据类型();
Student stu = new Student();
System.out.println(stu.age);
System.out.println(stu.name);
System.out.println(stu.stuId);
stu.run();
stu.study();
}
}
// import表示声明或者导入,也就是所需的类(Demo)导入当前(StudentTest)类下
// 这是功能声明的第一种方式,就是在当前类的前面进行import声明
import com.baidu.Demo;
public class StudentTest {
public static void main(String[] args) {
// 要访问Student类的信息(成员变量、成员方法)需要实例化一个对象
Student stu = new Student();
// 访问对象的信息(成员变量、成员方法)
stu.age = 21;
stu.name = "张三";
stu.stuId = 7;
System.out.println(stu.age);
System.out.println(stu.name);
System.out.println(stu.stuId);
stu.run();
stu.study();
Demo demo = new Demo();
// 第二种声明方式,不推荐使用每次建对象都需要写包名
// com.baidu.Demo demo = new com.baidu.Demo();
}
}
public class StudentTest {
public static void main(String[] args) {
// 要访问Student类的信息(成员变量、成员方法)需要实例化一个对象
Student stu = new Student();
// 访问对象的信息(成员变量、成员方法)
stu.age = 21;
stu.name = "张三";
stu.stuId = 7;
method(stu);
}
public static void method(Student stu) {
System.out.println("接受到的是地址信息:" + stu);
System.out.println(stu.name);
System.out.println(stu.age);
System.out.println(stu.stuId);
}
}
返回值是对象的地址值
public class StudentTest2 {
public static void main(String[] args) {
Student s = getMethod();
System.out.println(s.name);
System.out.println(s.age);
System.out.println(s.stuId);
}
// getMethod方法每调用一次,就会生成一个Student对象信息
public static Student getMethod() {
Student stu = new Student();
stu.name = "张三";
stu.stuId = 12;
stu.age = 20;
return stu;
}
}
垃圾回收器(Garbage Collection,GC)是JVM自带的一个线程(自动运行着的程序),用于回收没有任何引用所指向的对象。【所谓的垃圾:就是在代码运行的过程中,存在的无用代码(是占内存,但是无法使用它)】
GC线程会从栈中的引用变量开始跟踪,从而判定哪些内容是正在使用的。若GC无法跟踪到某一块堆内存,那么这些就认为这块内存不再使用了即为可回收的。但是Java程序员不用担心内存管理,因为垃圾回收集器会自动进行管理。
内存泄漏是指,不再被使用的内存没有被及时的回收,严重的内存泄漏会导致占用过多的内存从而导致程序崩溃,在程序中应该尽量避免不必要的内存浪费
GC线程判断对象是否可以被回收的依据是该对象是否有引用来指向,因此当确定该对象不再使用时,应该及时将其引用设置为null这样,该对象即不再被引用,属于可回收的范围。
GC的回收对程序员来说是透明的【这个功能Java成员独享】并不一定一发现有无用的对象就立即回收。一般情况下当我们需要GC线程即刻回收无用对象时,可以调用System.gc()方法。此方法用于建议JVM马上调度GC线程回收资源,但具体的实现策略取决于不同的JVM系统。
[alert color=“red”]注意[/alert]
import java.util.ArrayList;
import java.util.List;
/**
* 类:是对事物的一个抽象
* 修饰词 class 类名 {}
* public class Demo(){}
* 对象:实例化一个抽象的事物【依据类来创建对象,类型是对象模板】
* 数据类型 对象名[引用名] = new 数据类型();
* Demo demo = new Demo();
* (1)成员变量
* 数据类型 成员变量名;
* int age;
* String name;
* 注意:成员变量是有默认值的
* (2)成员方法
* /签名 = 方法名+参数列表/
* 修饰词 返回值类型 方法名(参数列表){}
* public void method(){}
* 内存管理
* Jvm = 方法区、栈、堆
* 方法区:
* 存储.class相关的内容
* 成员变量、成员方法【成员方法是有存储地址值的】
* 栈【特点:先进后出】:main方法是最先进入的,也是最后关闭的
* 存储的如果是基本数据类型,直接存储的是值
* 存储的如果是引用数据类型,存储的是引用的地址值
* 栈会位每一次执行的方法,开辟一块空间。名叫“栈帧”。生命周期:方法被调用时开始,到方法最后一句代码结束
* 局部变量也存储在栈中【因为方法在栈中,所以局部变量也在】。生命周期:从方法创建时开始,到方法最后一句代码结束
* 堆【new出来的】:
* 存放的是对象的值【每一个存储的对象在堆中都是一个唯一的地址值和栈中的引用地址值一致】
* 成员变量存储在堆中【因为对象在堆中,所以成员变量也在】。生命周期:从对象被创建开始,到变量回收为止
* gc();
* 是系统基本的线程
* gc对程序员来讲是透明的,我们是可以通过建议JVM的方式来实现垃圾的回收:System.gc();
* 什么是垃圾:
* 没有引用所指向的对象,就会被视为垃圾
* Cell c = new Cell();
* c = null;
* 此时,堆中存储的Cell对象就是一个垃圾!
* 如果不可用的对象在堆中存储过多,就会出现内存溢出的现象java.lang.OutOfMemoryError:Java heap space
* 所以,建议在某个对象使用之后,再也不会被用到的情况下,一定要进行null值处理[c=null;]
*/
public class Summary {
public static void main(String[] args) {
List<byte[]> list = new ArrayList<>();
int i = 0;
while(true) {
list.add(new byte[100 * 1024 * 1024]);
System.out.println("分配次数:" + (++ i));
}
}
}
在Java语言中,命名冲突问题是用包(Package)的概念来解决的,也就是说,在定义一个类时,除了定义类的名称一般还要指定一个包的名称,定义包名的语法如下
// 类的全称:包名.类名
// package 包名; 全小写
package test;
public class Test {
}
// 上述的Test类的全称为:test.Test
在命名包名的时候,包名可以有层次结构,在一个包中可以包含另外一个包,即:
// package 包名1.包名2....包名;
package test1.test2;
[alert color=“blue”]四个部分[/alert]
第一部分:公司或者组织域名的反写
第二部分:项目的名称
第三部分:模块名称
第四部分:类名
如下述定义可以分为4个部分,其中,StringUtil是类名,org.apache.commons.lang是多层包名,其含义如下:org.apache表示公司或组织的信息(是这个公司或组织域名的反写);commons表示项目的名称信息;lang表示模块的名称信息
org.apache.commons.lang.StringUtil
[alert color=“red”]注意[/alert]
为了避免类名的冲突问题,在声明类时指定的包名,这时对该类的访问就需要使用如下所示的全称
org.whatisjava.core.Point p = new org.whatisjava.core.Point();
可以看到,如上的书写方式过于繁琐不便于书写,为了解决这个问题,可以通过import语句对类的全称进行声明import语句语法如下:
// import 类的全局限定名(即包名+类名)
import org.whatisjava.core.Point;
这样就可以直接通过类名来访问了
[alert color=“red”]注意[/alert]
import org.whatisjava.core.*;
在软件系统中,常常通过封装来解决上面的问题。即:将容易变z的、具体的实现细节(卖水果或者钱)封装起来,外界不可访,而对外提供可调用的、稳定的能(店员或者柜员)这样的意义在于:
面向对象编程语言是对客观世界的模拟,客观世界里成员变量都是隐藏在对象内部的,外界无法直接操作和修改。封装可以被认为是一个保护屏障,防止该类的代码和数据被其他类随意访问。要访问该类的数据,必须通过指定的方式。适当的封装可以让代码更容易理解与维护,也加强了代码的安全性。
[alert color=“blue”]封装体现在两个方面[/alert]
/**
* @author codeWing23
* private修饰词的封装
* 1. private可以修饰方法,也可以修饰成员变量,不可以修饰类
* 所表示的意思是:私有的,不可以公开的,只能供本类使用
* 如果使用private来修饰,那么该成员变量或者成员方法的使用范围只能是在本类中
* 如果超出了本类就不能够直接访问,但是可以间接访问,提供了一对方法:getXXX(); setXXX(参数);
* 对于set和get方法而言,是有一定的规则的
* get方法,不能有参数、有返回值类型和成员变量对应
* set方法,不能有返回值,有参数彩且类型和成员变量对应
*/
public class PermissionDemo {
// 成员变量
String name;
private int age;
// 提供一个对私有成员变量age的设置方法
public void setAge(int num) {
if(num < 0 || num > 120) {
System.out.println("年龄不合法!");
}else {
age = num;
}
}
// 提供一个对私有成员变量age的访问方法
public int getAge() {
return age;
}
}
public class Test {
public static void main(String[] args) {
PermissionDemo permiss = new PermissionDemo();
permiss.setAge(-60);
System.out.println(permiss.getAge());
}
}
public class Teacher {
// 成员变量 写修饰词的快捷方式:alt 拖动鼠标
private String name;
private int age;
private boolean male;
// 成员方法
public String getName() {
return name;
}
public void setName(String name1) {
name = name1;
}
public int getAge() {
return age;
}
public void setAge(int age1) {
age = age1;
}
// 如果属性是boolean类型,一般get方法名称是写:isXXX();
public boolean isMale() {
return true;
}
public void setMale(boolean male1) {
male = male1;
}
}
public class PermissionDemo2 {
public static void main(String[] args) {
Teacher tea = new Teacher();
tea.setName("张三");
tea.setAge(20);
tea.setMale(true);
System.out.println(tea.getName());
System.out.println(tea.getAge());
System.out.println(tea.isMale());
}
}
/**
* this:就是表示当前的类的对象,就是指当前的成员是由那个对象调用的
* 格式:
* this.成员变量名
* 注意:当成员变量和局部变量没有冲突的情况下,this是存在的,只不过没有显示出来,写出来不会编 * 译错误
*/
public class Teacher {
// 成员变量 写修饰词的快捷方式:alt 拖动鼠标
private String name;
private int age;
private boolean male;
// 成员方法
public String getName() {
return name;
}
public void setName(String name) {
// 就近原则,当方法的局部变量和类的成员变量名称相同时,优先使用局部变量
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age1) {
age = age1;
}
// 如果属性是boolean类型,一般get方法名称是写:isXXX();
public boolean isMale() {
return true;
}
public void setMale(boolean male1) {
male = male1;
}
}
JavaBean是Java语言编写类的一种标准规范,符合JavaBean的类,要求类必须是具体的公共的,并且具有无参数的构造方法,提供用来操作成员变量的set和get方法
public class ClassName {
// 数据私有,方法公开
// 成员变量
// 构造方法
// 无参构造方法【必须】
// 有参构造方法【建议】
// 成员方法
// getXXX();
// setXXX();
}
当一个对象被创建时,怎么去创建,依据的就是构造方法,且构造方法可实现该对象的初始化。
[alert color=“red”]注意[/alert]
无论你是否显示的定义构造方法,所有的Java类都是有默认的构造方法的,因为Java自动提供一个无参构造方法,值得注意的是,如果一旦自己定义了构造方法,Java自动提供的默认无参构造方法就会失效。
构造方法是在类中定义的方法,但不同于其他的方法,构造方法的定义有如下两点规则:
public class Student {
// 这个构造方法是无参的,无参构造方法是jdk默认提供的
public Student() {
}
public static void main(String[] args) {
// 创建当前类Student的对象
Student stu = new Student();
}
}
访问修饰符 类名(参数列表) {
// 构造方法体
}
/**
* 构造方法的特点:
* 构造方法是专门用于创建对象的方法有以下两个特点
* 1. 方法没有返回值类型
* 2. 方法的名称和当前类名称相同
* 格式:
* public 类名(参数列表) {
*
* }
* 注意:
* 1. 构造方法是可以发生重载的【重载发生在同一个类中,方法名相同,参数列表不同(顺序不同,个数不同,类型不同 )
* 2. 如果没有撰写任何的构造方法,jdk会帮我们自动添加一个无参构造方法,一旦显示的添加一个构造方法,默认的失效
* 3. 构造方法不能return一个具体返回值
* 4. 构造方法不能有返回值类型,连void都不能写
* 5. 构造方法的名称一定要和类名相同
*/
public class PermissionDemo3 {
public static void main(String[] args) {
Student stu = new Student(100);
Student stu1 = new Student();
}
}
public class Student {
// 成员变量
String name;
int age;
//构造方法
// 如果自定义一个有参构造,则默认的无参构造将失效
public Student(int a) {
}
public Student() {
}
// 成员方法
}
/**
* 构造方法的特点:
* (1)构造方法是专门用于创建对象的方法有以下两个特点
* 1. 方法没有返回值类型
* 2. 方法的名称和当前类名称相同
* (2)实现对象的初始化【指的是对对象成员变量进行赋初始值】
* 格式:
* public 类名(参数列表) {
*
* }
* 注意:
* 1. 构造方法是可以发生重载的【重载发生在同一个类中,方法名相同,参数列表不同(顺序不同,个数不同,类型不同 )
* 2. 如果没有撰写任何的构造方法,jdk会帮我们自动添加一个无参构造方法,一旦显示的添加一个构造方法,默认的失效
* 3. 构造方法不能return一个具体返回值
* 4. 构造方法不能有返回值类型,连void都不能写
* 5. 构造方法的名称一定要和类名相同
*/
public class PermissionDemo3 {
public static void main(String[] args) {
Student stu = new Student();
stu.setName("Lisa");
stu.setAge(20);
System.out.println(stu.getName());
System.out.println(stu.getAge());
System.out.println("==========================");
/*
1. 创建对象的同时,对对象的属性进行赋值操作:Lisa,20
*/
Student stu1 = new Student("Alice", 50);
System.out.println(stu1.getAge());
System.out.println(stu1.getName());
}
}
public class Student {
// 成员变量
private String name;
private int age;
// 无参构造方法
public Student() {
}
// 有参构造方法
public Student(String name, int age) {
this.name = name;
this.age = age;
}
// 成员方法
public String getName() {
return name;
}
public void setName(String name1) {
name = name1;
}
public int getAge() {
return age;
}
public void setAge(int age1) {
age = age1;
}
}
继承Java面向对象编程技术的一块基石,因为它允许创建分等级层次的类
继承就是子类继承父类的特征和行为,使得子类对象(实例)具有夫类的实例域和方法,或子类从父类继承方法使得子类具有父类相同的行为。
在Java中通过关键字
extends
可以声明一个类是从另一个类继承而来的,一般形式如下:
// class 父类 {
public int a;
private int b; // private修饰属性和方法只能够在本类中和使用
}
// class 子类 extends 父类 {
public void sum {
a ++;
}
}
class Persion {
}
class dog extends Persion { // 不符合常理,就不能实现继承关系!
}
// 这种关系要符合is-a【是一个的关系】
其中多个子类可以称为子类,单独的那个类叫父类、超类或者基类
就是子类继承父类的属性行为,使得子类对象具有与父类相同的属性、相同的行为。子类是可以直接访问父类中的非私有的属性和行为。
在面向对象语言中,继承是必不可少的,主要有以下几个优点
/**
* 超类中私有的属性或者方法,是不能被子类继承的,进而无法方法该私有属性和方法
* 可以通过get、set方法来简介访问
*/
// 定义超类(父类、基类)
public class Person {
String name; // 名字
int age; // 年龄
String id; // 身份证号
private String slaNO;
public void eat(String name) {
System.out.println(name + "吃饭!");
}
public String getSlaNO() {
return slaNO;
}
public void setSlaNO(String slaNO1) {
slaNO = slaNO1;
}
}
public class Student extends Person{
public static void main(String[] args) {
Student stu = new Student();
// Student类中并没有显示的写任何成员变量和成员方法,但是通过继承关系,就能够继承成员属性和成员方法
stu.age = 18;
stu.id = "123456789123456798";
stu.name = "李四";
stu.setSlaNO("200000");
stu.eat(stu.name);
System.out.println(stu.id);
System.out.println(stu.name);
System.out.println(stu.age);
System.out.println(stu.getSlaNO());
}
}
/**
* 这是一个父类(超类、基类)
*/
public class Fu {
int aFu = 10;
// 父类的成员变量
int b = 20;
public int getB() {
return b;
}
}
/**
* 这是一个子类
*/
public class Zi extends Fu{
int aZi = 10;
// 子类的成员变量
int b = 30;
public void method() {
// 局部变量
int b = 22;
System.out.println(b);
// 那个对象调用了当前的method方法,那么this就指代的是那个对象【this可以是很多个】
System.out.println(this.b);
// 子类继承了那个父类,那么super就指代的是哪个父类的对象【super只有一个】
System.out.println(super.b);
}
}
/**
* 在继承关系中,对象能够调的东西,优先是在本类中查找
* ===================
* 子类成员变量:this.成员变量名
* 父类成员变量:super.成员变量名
* 局部变量:直接变量名
*/
public class Test {
public static void main(String[] args) {
// 创建Fu类
/*
创建父类的对象,通过他的引用(变量名称、对象名)能够调到所有非私有的成员变量,成员方法
*/
Fu fu = new Fu();
System.out.println(fu.aFu);
System.out.println(fu.b);
// 创建Zi类
/*
在父子类继承关系中,如果成员变量的名称相同,在创建了子类对象之后,直接调用到的成员变量是子类的。
如果创建子类的对象之后,一定要访问父类的成员变量,可以间接的进行访问【get、set】
*/
System.out.println("==============");
Zi zi = new Zi();
System.out.println(zi.aFu);
System.out.println(zi.aZi);
System.out.println(zi.b);
System.out.println(zi.getB());
System.out.println();
System.out.println("==================");
Zi zi1 = new Zi();
zi1.method();
}
}
/**
* 成员方法的应用
* 【重写方法】
* 概念:
* 在继承关系中,方法名称相同,参数列表也相同的方法
*
* 特点:
* 如果使用父类的引用指向子类的对象的方式类调用重写的方法,要注意看该方法是否在子类中发生重写,如果方法重写,那么得到的结果是子类中重写的
*/
public class Test {
public static void main(String[] args) {
/*
*子类====自己的成员变量、父类非私有的成员变量、自己成员方法、父类非私有的成员方法
* 变量如果重名,子类可以直接方法自己的,也可以间接访问父类
* 方法如果重名,子类可以直接访问自己的,也可以间接的访问父类,(将父类重名的方法间接的放置在另一个方法中,子类继承了也就访问了)
* 父类====能够访问的是自己的成员变量和成员方法
* 父类能不能访问到子类成员变量? ===不可以
*
* 父类能不能访问到子类成员方法? ===只有被重写的方法才可以父类访问
*/
// 父类调父类
Fu fu = new Fu();
System.out.println(fu.getB());
// 子类调子类
Zi zi = new Zi();
System.out.println(zi.getB());
// 子类间接调父类
zi.get();
// 父类调子类
System.out.println("=============");
Fu fu1 = new Zi();
System.out.println(fu1.aFu);
System.out.println(fu1.b);
// 此处父类优先也是在自己类中进行了查找,但是又发现,子类对这个方法进行了重写,所以调用的是子类重写的
System.out.println(fu1.getB());
}
}
public class Zi extends Fu{
int aZi = 15;
// 子类的成员变量
int b = 30;
public int getB() {
return b;
}
public void method() {
// 局部变量
int b = 22;
System.out.println(b);
// 那个对象调用了当前的method方法,那么this就指代的是那个对象【this可以是很多个】
System.out.println(this.b);
// 子类继承了那个父类,那么super就指代的是哪个父类的对象【super只有一个】
System.out.println(super.b);
}
}
/**
* 【重写注意细节】
* 1.要发生重写,必须方法名称相同,参数列表相同。【签名相同】
* 2. 子类重写的方法前面是可以显示的添加@Override注解,表示这个方法是重写的方法,也可以省略
* 3. 子类重写的方法,父类的返回值类型如果是void/8种基本数据类型,必须保持一致
* 父类的返回值如果是引用数据类型,子类重写方法可以返回父类类型或者父类的子类型
* 4. 修饰词:如果父类的修饰词是public,子类也一定是public,子类方法的修饰词权限要比父类的大或者等于。【父类方法私有,子类无法重写!】
* 小拓展
* 权限: public(公开的) > protected(受保护的) > 默认的(default) > private(私有的)
* 注意:default是默认权限,就是什么都不用写,不能显示的写出来。
*/
public class TestPhone {
public static void main(String[] args) {
// 父类Phone的引用p,指向了子类NewPhone的对象
Phone p = new NewPhone();
p.call();
p.sendMessage();
p.showNum(); // 调到的showNum是子类NewPhone重写之后的
System.out.println("-------------------------");
NewPhone newPhone = new NewPhone();
newPhone.call();
newPhone.showNum();
newPhone.sendMessage();
System.out.println("--------------------------");
// 父类自己的引用指向的是自己的对象
Phone phone = new Phone();
phone.call();
phone.showNum();
phone.sendMessage();
}
}
/**
* 方法上面添加了@Override注解 表示含义是该方法是重写方法
* @Override如果报错,两种可能:第一就是该注解放在了不是重写的方法上面,第二个就是单词写错了
* 方法不写修饰词public,那么就是默认权限(default),并不是关键词default
* protected 受保护!
*
*/
// 这是子类
public class NewPhone extends Phone{
@Override
public NewPhone showNum() {
// super 表示父类
// 当前的对象调用了这个方法,那么也会调用到父类的该show方法
super.showNum();
System.out.println("显示照片");
System.out.println("显示姓名");
// return new NewPhone(); //如果返回值类型是父类,那么return后面返回的值可以父类类型,也可以是子类类型
return new NewPhone();
}
}
/**
*方法的重写与重载
* 1. 重写:Override
* (1)发生在有继承关系的父子类之间
* (2)要发生重写,方法名称和参数列表一定要保持一致
* (3)返回值类型:
* 如果方法的返回值类型是:void和8种基本数据类型,返回值类型一定要保持一致!
* 如果方法的返回值类型是:引用数据类型,子类方法的返回值类型可以小于或者等于父类的返回值类型
* (4)修饰词:
* 子类方法的访问权限要大于或者等于父类访问权限。【父类=public情况,子类也一定是public。父类的private方法子类无法继承,就不可以重写】
* ===== 重写遵循“运行期绑定”
* 2, 重载:Overload
* (1)发生在一个类中
* (2)要发生重载,方法名一定要相同,参数列表一定要不用【个数的不同、类型的不同、顺序的不同】
* (3)对于返回值类型无要求
* (4)对修饰词无要求
* ===== 重载遵循“编译期绑定”
*/
public class OverLoadAndOverride {
public static void main(String[] args) {
// 父类的引用指向子类的对象
Super obj = new Sub(); // 这样格式的对象创建我们又称为”向上造型“
obj.f(); // sub.f();
System.out.println("----------------");
Goo goo = new Goo();
goo.g(obj);
}
}
class Super {
public void f() {
System.out.println("super.f()");
}
}
class Sub extends Super {
@Override
public void f() {
System.out.println("sub.f()");
}
}
class Goo {
public void g(Super obj) {
System.out.println("g(Super)");
}
public void g(Sub obj) {
System.out.println("g(Sub)");
}
}
/**
* 在继承关系中,父子类构造方法访问需要注意:
*
* 1. 子类构造方法种是默认一个父类的无参构造的调用【super()】
* 所以就可以很好的解释:在构造子类的时候,优先构造父类
* 2. 子类是可以通过关键字super进行父类构造方法的调用。在一个构造方法种,super调用只能调用一次
* 3. super调用的构造方法,必须位于子类构造方法的第一行
* 4. 如果父类种没有无参构造,那么子类构造方法就不会隐式提供super()方法
*/
public class ConstructionDemo {
public static void main(String[] args) {
// 创建子类的对象
Zi zi = new Zi();
}
}
class Fu {
public Fu(int a) {
System.out.println("父类的构造方法开始执行");
}
int num = 10;
}
class Zi extends Fu{
public Zi() {
super(1);
// 隐藏了一个super();
// super(); 调用的是父类的无参构造
System.out.println("子类的构造方法开始执行");
}
int num = 20;
}
/**
* super关键字主要用于以下三种情况:
*
* 1. 在子类的成员方法中,访问父类的成员变量
* 2. 在子类的成员方法中,访问父类的成员方法
* 3. 在子类的构造方法中,访问父类的构造方法
*/
public class SuperDemo {
public static void main(String[] args) {
}
}
class Fu {
int num = 1;
public void method() {
System.out.println("父类的method方法!");
}
}
class Zi extends Fu{
int num = 2;
public Zi() {
super();
}
public void method1() {
System.out.println("访问了父类的成员变量" + super.num);
}
@Override
public void method() {
super.method();
System.out.println("子类的method方法!");
}
}
/**
*
* this关键字主要用于以下三种情况:
*
* 1. 在本类的成员方法中,可以访问本类的成员变量
* 2. 在本类的成员方法中,访问本类的其他成员方法
* 3. 在本类的构造方法中,可以调用本类的其他构造方法【注意多个构造方法间的调用,避免死循环】
*
* 注意:构造方法是不可以自己调用自己的构造方法的
* super关键字是用来访问父类的内容
* this关键字是用来访问本类的内容
*/
public class ThisDemo {
public static void main(String[] args) {
Zi zi = new Zi();
zi.showMessage();
}
}
class Fu {
int num = 1;
}
class Zi extends Fu{
int num = 2;
public Zi() {
this(1);
}
public Zi(int num) {
this.num = num;
}
public void showMessage() {
int num = 3;
System.out.println(num); // 3
System.out.println(this.num); // 2
System.out.println(super.num); // 1
}
public void method1() {
System.out.println("method1()");
}
public void method2() {
this.method1();
showMessage();
System.out.println("method2()");
}
}
Java继承特性的三个特点
Java语言是单继承性的【单根性】,也就是一个类的直接父类只能有一个
Java可以多级继承,即继承具有传递性,就是:儿子可以有爸爸,爸爸也可以有爸爸
一个子类的直接父类是唯一的,但是一个父类的子类是可以有多个的
由abstract修饰的方法为抽象方法,抽象方法即只有方法的定义,没有方法体实现,用一个分号结尾,即方法五要素中,抽象方法缺少了一个要素(即:方法体),也可以将抽象方法理解为不完整的方法。
若将抽象方法包含在类中,即该类也应该为抽象的,可以理解为,该类也不完整,抽象类由abstract关键字声明。
抽象方法:没有方法体的方法
格式:
public abstract 返回值 方法名(){};
抽象类:包含抽象方法的类
格式:
public abstract class 类名{}
public class AbstractDemo {
public static void main(String[] args) {
// 创建Animal抽象类的对象
Animal animal = new Pig();
animal.eat();
animal.sleep();
System.out.println("--------------");
Pig pig = new Pig();
pig.eat();
pig.sleep();
System.out.println("-------------");
Dog dog = new Dog();
dog.eat();
dog.sleep();
System.out.println("--------------");
Animal animal1 = new Dog();
animal1.sleep();
animal1.eat();
}
}
/**
* 抽象方法:加关键字abstract,并且方法没有具体实现(方法体)
* 抽象类:含有了抽象方法的类,需要在class关键字之前添加abstract
* 注意:
* 1. 抽象类是不能够进行实例化的(不能创建对象)
* 2. 抽象类必须具有一个子类进行继承
* 3. 子类继承了抽象类之后,一定要实现抽象类的所有抽象方法,如果不实现,子类也必须是抽象类
* 4. 抽象类是构造方法的。目的是供子类创建对象时初始化父类的成员变量
* 5. 抽象类不一定抽象方法,但是包含抽象方法的类是抽象类
*/
public abstract class Animal {
public abstract void eat();
public abstract void sleep();
}
public class Pig extends Animal{
// 普通的成员方法,是对父类抽象方法的覆盖(重写)
@Override
public void eat() {
System.out.println("eat...");
}
@Override
public void sleep() {
System.out.println("sleep...");
}
}
public class Dog extends Animal{
@Override
public void eat() {
System.out.println("eat1...");
}
@Override
public void sleep() {
System.out.println("sleep1...");
}
}
假设创建了抽象类的对象,调用抽象的方法,而抽象的方法没有具体的方法体,没有意义。
子类的构造方法中,有默认的super(),需要访问父类构造方法。
未包含抽象方法的抽象类,目的就是不想让调用者创建该类的对象,通常用于某些特殊的类结构设计。
假设不重写所有的抽象方法,则类中可能包含抽象方法,那么创建对象后,调用抽象的方法,没有意义。
接口就是一种统一的规范标准,不同的实现厂商可能实现的过程和原理有所差别,但遵守的接口是一致的!有了统一化的标准接口,使得事物间的沟通性增强。
接口,是Java语言中一种引用类型,是方法的集合,如果说类的内部封装了成员的变量,构造方法和成员方法,那么接口的内部主要就是封装了方法,包含抽象方法(JDK7及以前),默认(default)方法和静态(static)方法(JDK8),私有(private)方法(JDK9)
抽象方法:使用abstract关键字修饰,可以省略,没有方法体。该方法供子类实现使用。
默认方法:使用default修饰,不可省略,供子类调用或者子类重写。
私有方法:使用private修饰,供接口中的默认方法或者静态方法调用。
接口的定义,它与定义类方法相似,但是使用interface关键字,它也会被编译成class文件,但一定要明确它并不是类,而是另外一种引用数据类型。
引用数据类型:数组、类、接口。
接口可以看成是特殊的抽象类。抽象方法和常量可以省略public abstract。因其默认就是public abstract的。
接口的使用,它不能创建对象,但是可以被实现(implements,类似于被继承)。一个实现接口的类(可以看作是接口的子类),需要实现接口中所有的抽象方法,创建该类的对象,就可以调用方法了,否则它必须是一个抽象类。
/**
* 接口是一个统一的公共规范【接口并不是一个类,同样也是可以编译为.class文件的,是java中的引用数据类型】
* 1. 对于接口中的常量和抽象方法,可以省略掉public abstract关键字的,因为默认就是public abstract!
*
*/
public interface Runner {
// 常量 - JDK7
// public final static int runBER_1 = 1;
int runBER_1 = 1;
// 抽象方法 - JDK7
// public void run(int a, int b);
void run(int a, int b);
// public default void run() {
// System.out.println("JDK8,接口中的默认方法!");
// }
// public static void run(int a) {
// System.out.println("JDK8,接口的静态方法!");
// }
// public static void run(int a, int b) {
// System.out.println("JDK8,接口的静态方法");
// }
// private void method() {
// System.out.println("JDK9,接口中的私有方法!");
// }
}
/**
* 1. 实现了接口的类,一定要重写/实现抽象方法
* 2. 实现类是可以继承和重写接口中的默认方法【重写默认方法,需要注意的是,要渠道default关键字】
* 3. 接口中静态方法的访问方式:接口名.静态方法();
* 4. 接口中的私有方法分为:普通私有方法和静态私有方法,其作用就是在本接口中使用,不对外暴露
*/
public class MyInterface01 implements Interface01{
@Override
public void method01() {
System.out.println("重写了method01方法");
}
@Override
public void method02() {
System.out.println("JDK8默认方法的重写");
}
public static void main(String[] args) {
MyInterface01 myInterface01 = new MyInterface01();
myInterface01.method01();
System.out.println("---------------");
// 当前的实现类继承了接口的默认方法!
myInterface01.method02();
Interface01.method04();
}
}
public interface Interface01 {
// public Interface01() {
//
// }接口没有构造方法
public abstract void method01();
// JDK1.8 default, static
public default void method02() {
System.out.println("JDK8,默认方法!");
}
public default void method03() {
System.out.println("JDK8,默认方法!");
}
public static void method04() {
System.out.println("JDK8静态方法");
}
}
格式:
public class InterfaceImp1 implements InterfaceA, InterfaceB {
// 覆盖重写所有抽象方法
}
格式:
public interface Interface extends InterfaceA, InterfaceB {
// 继承两个接口的所有抽象方法
// 要实现两个接口的重复的默认方法
}
注意事项:
同一行为的不同表现形式。
[admonition title=“注意” color=“red”]当使用多态方式调用方法时,,首先检查父类中是否有该方法,如果没有,则编译错误,如果有,执行的是子类重写后的方法。[/admonition]
父类的引用指向子类的对象,这个现象就是向上造型。
一个类的对象可以向上造型的类型有:父类的类型及其实现的接口类型,当发生向上造型时,Java编译器会根据类型检查调用方法是否匹配。
public abstract class Emp {
String name;
int age;
double sal;
public void clockUp() {
System.out.println("上班打卡!");
}
public void clockDown() {
System.out.println("下班打卡!");
}
public abstract void work();
}
public class Teacher extends Emp implements Consultant, Author{
@Override
public void work() {
System.out.println("讲师授课!");
}
@Override
public void edit() {
System.out.println("编辑文稿");
}
@Override
public void answer() {
System.out.println("解决企业问题");
}
@Override
public void training() {
System.out.println("培训企业员工");
}
public static void main(String[] args) {
Teacher teacher = new Teacher();
teacher.answer();
teacher.edit();
teacher.training();
teacher.work();
System.out.println("------------");
// 老师本身也是员工, 所有使用员工类型也是可以接受老师对象的。
// 使用向上造型方式来调用方法或者属性,注意一定得是父类或者接口拥有的。
Emp emp = teacher;
emp.clockDown();
emp.clockUp();
System.out.println("---------------");
Consultant consultant = teacher;
consultant.answer();
consultant.training();
System.out.println("---------------");
Author author = teacher;
author.edit();
}
}
public class Tutor extends Emp{
@Override
public void work() {
System.out.println("为学生答疑");
}
}
/**
* 顾问接口
*/
public interface Consultant {
public abstract void answer();
public abstract void training();
}
/**
* 编辑作者接口
*/
public interface Author {
public abstract void edit();
}
public abstract class Animal {
// 吃
public abstract void eat();
}
public class Dog extends Animal{
@Override
public void eat() {
System.out.println("狗吃骨头!");
}
void dogWork() {
System.out.println("看大门!");
}
}
public class Cat extends Animal{
@Override
public void eat() {
System.out.println("猫吃鱼!");
}
void catWork() {
System.out.println("捉老鼠!");
}
}
public class Demo {
public static void main(String[] args) {
Demo demo = new Demo();
Cat cat = new Cat();
Dog dog = new Dog();
demo.showCat(cat);
demo.showDog(dog);
System.out.println("-------------");
Animal animal1 = cat;
Animal animal2 = dog;
demo.showAnimal(animal1);
demo.showAnimal(animal2);
// animal1.catWork(); //编译错误,Animal中没有catWork
/*
向下转型:(强转)
*/
// 将Animal1向下转型
System.out.println("----------------");
Cat c = (Cat)animal1;
c.catWork();
Dog d = (Dog)animal2;
d.dogWork();
/*
编译报错:
java.lang.ClassCastException:类型转换异常
因为animal2是动物类型,所以是可以把动物类型强制转换为cat类型的,但是在运行过程中出现了业务逻辑问题
animal2本质是dog对象,是不可以把一只狗转换为一只猫
解决方案:
在进行类型转换之前,做一次必要的类型判断:使用关键字instanceof
格式:
变量名称 instanceof 类型
这是一个boolean类型的表达式,如果变量符合后面的类型,返回true,否则返回false
*/
// Cat c1 = animal2;
// c1.catWork();
System.out.println("-----------------");
if(animal2 instanceof Cat) { // 如果animal2是猫类型,则转换为猫类型
Cat c1 = (Cat)animal2;
c1.catWork();
}else if(animal2 instanceof Dog) { // 如果animal2是狗类型,则转换为狗类型
Dog d1 = (Dog)animal2;
d1.dogWork();
}
}
void showAnimal(Animal animal) {
animal.eat();
}
void showCat(Cat cat) {
cat.eat();
}
void showDog(Dog dog) {
dog.eat();
}
}
在所描述的事物,存在包含关系,就可以使用内部类这种结构:汽车类包含了发动机类,那么汽车类就是外部类,而发动机类就是描述汽车类的内部类,其身份就是一个成员。
格式:
class 汽车类 { // 汽车是外部类
class 发动机 { // 发动机是内部类
}
}
[alert title=“特点” color=“red”][/alert]
格式:
[外部类名].内部类名 对象名 - new 外部类型().new 内部类型()
/**
* 访问内部类的两种方式:
* 1. 间接访问:创建一个外部类的成员方法,包含内部类的对象,通过外部类来访问成员方法,进而来访问成员内部类
* 2. 直接访问:OutClass.InnerClass inner = new OUtClass().new InnerClass();
*
* 解决内部类结构中,变量名冲突问题:
* 1. 访问内部类的局部变量。直接调用即可
* 2. 访问inner类的变量,需要指定当前对象:this
* 3. 访问out类的变量,需要指定当前对象:Out.this
* 对于内部类而言是可以分为:成员内部类和局部内部类的
* “局部”:指的是在方法中存活的概念
* 局部内部类:
* class Out { // 外部类
* public void method() { // 成员方法
* class Inner { // 局部内部类
* *
* * }
* }
* }
*/
public class OutClass { // 外部类
String info = "这是外部类!";
public void show() { // 外部类的成员方法
InnerClass innerClass = new InnerClass();
innerClass.show();
System.out.println(info);
}
public class InnerClass { // 内部类【成员内部类】
String info = "这是内部类!";
public void show() {
String info = "这是内部类的局部变量!";
System.out.println(info); // 就近原则
System.out.println(this.info); // this表示当前类的对象,也就是内部类
// System.out.println(super.info); 编译报错、因为当前super指代的是Object,没有info变量
System.out.println(OutClass.this.info); // OutClass.this就是指明当前的类的对象是OutClass的
}
}
public static void main(String[] args) {
// 访问内部类:间接访问
OutClass outClass = new OutClass();
outClass.show();
System.out.println("----------------");
// 访问内部类:直接访问
OutClass.InnerClass inner = new OutClass().new InnerClass();
inner.show();
}
}
/**
* 局部内部类:
* 定义在方法中的类,只服务于当前的方法,对外是不可见的。这个类的生命周期对着方法调用开始,方法结束位置,也就是局部内部类供此方法来调用。
* 使用内部类需要注意:
* 1. 果内部类是局部内部类,那么修饰不可以添加
* 2. 在JDK8之后,局部变量在局部内部类中使用的时候,final是可以省略的。
*解释为什么局部内部类一定要使用的变量具备final性质:
* 1. 方法被调用,运行在栈(栈帧)
* 2. 对象运行在堆中
* 3. 当方法运行完毕,就会出栈(生命周期结束),方法消失了a就会消失,为了保证在堆中的对象能够正确的访问到方法给的值
* 那么就把这个值设置为常量,保证了数据的不可变性(方法存在和消失不会影响赋值)
*/
public class Out { // 外部类
public void method() {
int a = 10; // a放在常量池中
class Inner { // 局部内部类
public void method() {
System.out.println(a);
}
}
Inner inner = new Inner();
inner.method();
}
}
public class Test {
public static void main(String[] args) {
Out out = new Out();
out.method();
}
}
/**
* 匿名内部类的实现格式:
* 接口类型 对象名称 = new 接口名称(){
* // 实现当前接口中的抽象方法
* };
* 说明:
* 1. new是一个创建对象的动作【创建的是一个实现类的对象,目前这个实现类是没有名字的】
* 2. 接口类型就是匿名内部类要去实现的那个接口
* 3. {
* // 匿名内部类要执行的内容
* };
* 注意:
* 1. 匿名内部类在创建完对象后,只能使用唯一的一次
* 2. 匿名对象的方式不推荐使用,因为调用的方法也只能够调用一次,而且只能调用第一个实现的抽象方法。
*
* 什么时候使用匿名内部类?
* 当实现类只需要使用一次的时候,就优先考虑匿名内部类的方式
*/
public class MyDemo {
public static void main(String[] args) {
// 直接创建实现类的对象
InterfaceImpl anInterface = new InterfaceImpl();
anInterface.show();
// 多态的方式进行调用
Interface anInterface1 = new InterfaceImpl();
anInterface1.show();
System.out.println("-------------------");
// 匿名内部类的实现方法:anInterface2是对象的名称
Interface anInterface2 = new Interface() {
// 这个过程就是对接口中的抽象方法的重写
@Override
public void show() {
System.out.println("匿名内部类:实现类接口中的抽象方法!1111");
}
};
anInterface2.show();
System.out.println("-----------------");
// 匿名对象:在调用方法的时候,只能调用唯一的一次,只能调用唯一的方法
new Interface() {
// 这个过程就是对接口中的抽象方法的重写
@Override
public void show() {
System.out.println("匿名内部类:实现类接口中的抽象方法!2222");
}
}.show();
}
}
public class InterfaceImpl implements Interface{
@Override
public void show() {
System.out.println("实现了接口中的抽象方法!");
}
}
public interface Interface {
public void show();
}
Java中访问修饰(public、private、protected、default)都可以修饰成员,权限如下:
修饰符 | 本类 | 同一个包中的类 | 子类 | 其他类 |
---|---|---|---|---|
public | 可以访问 | 可以访问 | 可以访问 | 可以访问 |
protected | 可以访问 | 可以访问 | 可以访问 | 不能访问 |
default | 可以访问 | 可以访问 | 不能访问 | 不能访问 |
private | 可以访问 | 不能访问 | 不能访问 | 不能访问 |
[alert title=“注意” color=“red”][/alert]
这里的子类是不同包中的子类
其中,public修饰符,在任何地方都可以访问;protected可以在本类、同一包中的类、子类中访问,除此之外的其他类不可以访问;默认方式为可以本类及同一包中的类访问,除此之外其他类不可以访问;private只可以在本类中访问,其他任何类都不可以访问。
[alert title=“回顾” color=“blue”][/alert]
private与public为最最常用的两个访问控制修饰符,其中,private修饰的成员变量和方法仅仅只能在本类中调用,而public修饰的成员变量和方法可以在任何地方调用。
final关键字修饰变量,意为不可改变。final可以修饰成员变量,也可以修饰局部变量,当final修饰成员变量时,可以有两种初始方式:
final关键字修饰局部变量,在使用之前初始化即可。参加如下示例:
public class Emp {
private final int no = 100; // final成员变量声明时初始化
public static void main(String[] args) {
no = 99;
}
}
static关键字可以修饰成员变量,它所修饰的成员变量不属于对象的数据结构,而是属于类的变量,通常通过:类名来引用static成员。
当创建对象后,成员变量是存储在堆中的,而static成员变量和类的信息一起存储在方法区,而不是在堆中,一个类的static成员变量只有“一份”(存储在方法区),无论该类创建了多少对象,看如下示例:
class Cat {
private int age;
private static int numOfCats;
public Cat(int age) {
this.age = age;
System.out.println(++ numOfCats);
}
}
// 在main方法中声明两个Cat类对象;
Cat c1 = new Cat( 2);
Cat c2 = new Cat( 3);
注:如果您通过阅读本文解决了问题,恳请您留下一个宝贵的赞再离开,如果不嫌弃的话,点个关注再走吧,非常欢迎您能够在讨论区指出此文的不足处,博主会及时对文章加以修正 !如果有任何问题,欢迎评论,非常乐意为您解答!( •̀ ω •́ )✧