java基础
2021/9/4 11:08:27
本文主要是介绍java基础,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
计算机DOS命令
- 硬盘切换:字母+分号
- 查看当前目录下的所有文件:dir
- 切换目录:(change directory )cd /d 字母+分号
- 进入硬盘某文件夹:cd /d 字母+分号 \ 文件名
- 从硬盘进入文件夹:cd 文件名
- 返回上一级:cd …/
- 清空屏幕:cls (clear screen)
- 退出终端:exit
- 查看本机ip:ipconfig
- 进桌面:cd /d 桌面地址
- 创建文件夹:(make)md 文件夹名
- 进入文件夹:cd 文件夹名
- 文件夹内建文件:cd>文件名.格式
- 删除文件:del 文件名.格式
- 删除文件夹 :(remove) rd 文件名
- print(); 输出后不换行
- println(); 输出后换行
1.java基础
java三高 :高可用、高性能、高并发
1.1.JDK JRE JVM
- JDK: Java development kit
- JRE: Java runtime environment
- JVM: Java virtual machine
测试jdk是否安装成功 cmd 输入 java -version
1.2.第一个java程序
- 随便新建一个文件夹,存放代码
- 新建一个java
- 文件后缀名为 .java
3.编写代码
- 文件后缀名为 .java
public class Hello{ public static void main(String[] args){ System.out.print("Hello world!"); } }
4.编译javac Hello.java文件,会生成一个java文件
5.运行class文件,java class名
1.3.java程序运行机制
- 编译型:执行速度快,对操作系统要求低,用编译器对整个程序编译一次就能用了
- 解释型:边执行边解释,走一步执行一步,性能有损失
java两种都包含,只是时机不同
- 1.预编译:java文件经过java编译器的javac命令变成class文件
- 2.生成一个机器码和java源代码之间的一个程序(字节码文件)
- 3.字节码被执行的时候进入JVM虚拟机 ,把class文件这个类放到虚拟机的类装载器里,类就会加载到JVM里。
- 4.字节码校验器对代码进行检验是否有误
- 5.再通过解释器解释给操作系统
文档注释
// JavaDOC:文档注释 /** */ /** * @description helloworld * @Author 作者 */
标识符注意点
java 是强类型语言 :要求变量的使用要严格符合规定,所有的变量都必须先定义后才能使用
一旦定义了一个变量指定了某个数据类型,如果不经过转换的话他就永远是这个类型
- 好处:安全性高 缺点: 速度慢
- 八大基本数据类型
// 整数 int num1 = 10; // 最常用 byte num2 = 21; short num3 = 30; long num4 = 30L; // long类型要在数字后面加个L // 小数:浮点数 float num5 = 50.1F; // float类型要在数字后面加个F double num6 = 3.1415926; // 字符 char name = '中'; // 字符串,String不是关键字,是类 // String namea = "chaoren"; // 不是基本类型 // 布尔值: boolean flag = true; boolean Flag = false;
1.4.数据类型
public class Demo03 { public static void main(String[] args){ int i = 0; int i1 = 010; int i2 = 0x11; // 十六进制0x 0~9 A~F:10~15 System.out.println(i); System.out.println(i1); System.out.println(i2); System.out.println("==========="); // 浮点数拓展 浮点数不能表示精确的数值,不能用于比较和表示货币等 // 银行业务怎么表示? 用 BigDecimal 数学工具类 // float 有限 离散 舍入误差 约等于 接近但不等于 // double // 得出结论:最好完全避免使用浮点数进行比较 float f = 0.1f; // 0.1 double d = 1.0/10; // 0.1 System.out.println(f==d); // false float d1 = 23413153515f; float d2 = d1 + 1; System.out.println(d1==d2);// true // 字符拓展 System.out.println("============"); char c1 = 'a'; char c2 = '中'; System.out.println(c1); System.out.println((int)c1); // (int) 表示强制转换 System.out.println(c2); System.out.println((int)c2); // 强制转换 // 所有字符本质还是是数字 // char类型涉及到编码类型问题 里的Unicode编码可以处理各种语言文字 // 占两个字节 最多可表示65535个字符 0~65535 2^16=65535 // unicode 表:(97=a 65=A) unicode转义编码表示范围:U0000~UFFF char c3 = '\u0061'; // \u 表示转义字符 System.out.println(c3); // 转义字符 \t 水平制表符 \n 换行符 System.out.println("========"); String sa = new String(original:"helloworld"); String sb = new String(original:"helloworld"); System.out.println(sa==ab); // false String sc = "helloworld"; String sd = "helloworld"; System.out.println(sc==sd); // true // 对象 从内存分析 } }
- 运算中,不同类型的数据先转化为同一类型,让后进行运算
1.5.类型转换
不同类型混合运算不论是是赋值还是运算都是向定义时的最高位类型转
// 类型转换 public class Demo05 { public static void main(String[] args){ int i = 128; byte b = (byte)i; // 内存溢出 byte类型数值范围:-128~127 输出垃圾值 // 强制转换 (类型)变量名 高---低 int j = 128; double c = j; // 自动转换 低---高 System.out.println(i); System.out.println(b); System.out.println(j); System.out.println(c); /* 注意点: 1.不能对布尔值进行转换 2.不能把对象类型转换为不相干的类型 3.在把高容量转换到低容量的时候 用强制转换 4.转换的时候可能存在内存溢出或精度问题! */ System.out.println("========"); System.out.println((int)23.7); // double类型 23 System.out.println((int)-45.89f); // float类型 -45 System.out.println("========"); char e = 'a'; int f = e+1; System.out.println(f); System.out.println((char)f); System.out.println("========="); // 操作比较大的数时 注意溢出问题 // jdk7新特性,数字之间可以用下划线分割 int money = 10_0000_0000; // System.out.println(money); // 1000000000 int years = 20; int total = money*years; // 负数,计算时溢出了 long total2 = money*years; // 赋值前默认是int,转换之前已经存在问题了 long total3 = money*((long)years);// 先把一个数转换为long System.out.println(total3); } }
1.6.变量
- java变量是程序中最基本的存储单元,其要素包括变量名,变量类型和作用域。
- 注意事项:
- 每个变量都有类型,类型可以是基本类型,也可以是引用类型(例如String)
- 变量名必须是合法的标识符
- 变量声明是一条完整的语句,因此每一个声明都必须以分号结束
public class Demo08 { // 这是类 // 类变量前加 static // 他是从属于Demo08这个类 而且会随着该类一起出来一起消失 static double salary = 2500; // 都先把类变量加个static好去调用 // 属性:变量 // 实例变量:从属于对象(类), 作用域在类里面 不在方法里面 如果不自行初始化 这个类型的默认值 所有数值类型一般都是:0,浮点型:0.0 (char)字符串变量默认值是0 或'\u0000' 而非 '0',布尔值默认值是false 除了基本类型,其余的默认值都是null String name; // 定义的变量直接可以用 但用起来不简单 int age; // 类里面会有个默认方法 main方法 public static void main(String[] args){ // 局部变量:必须声明和初始化值 int i = 10; System.out.println(i); // 变量类型 除了基本类型 类型也可以是自己(自定义类型)也是引用类型 这个变量名字他有个值叫做new了一个Demo08 相当于把自己(变量)给拿到了 // 通过这个demo08就可以用他的东西了 Demo08 demo08 = new Demo08(); // 相当于把Demo08这个类拿到了 他会通过这个类返回一个Demo08 System.out.println(demo08.age); System.out.println(demo08.name); // 类变量 System.out.println(salary); } // 其他方法 public void add(){ } }
1.7.常量
- 一经初始化 不能再修改
- 常量名一般使用大写字符
public class Demo09 { // 常量通过final这关键词来定义 常量名一般用大写表示 // static final 这俩是修饰符 不存在先后顺序 static final double PI = 3.14; public static void main(String[] args) { System.out.println(PI); } }
1.8.变量的命名规范
- 所有变量、方法、类名:见名知意
- 类成员变量:首字母小写和驼峰命名法:monthSalary 除了第一个单词外,后面的单词首字母大写
- 局部变量:同上
- 常量:大写字母和下划线:MAX_VALUE
- 类名:首字母大写和驼峰命名法 Man,GoodMan
- 方法名:首字母小写和驼峰命名法:run() runRun()
1.9.运算符 运算优先级用括号括起来
/ 号是取余数(模) %号是取商数
- 自增自减运算符
public static void main(String[] args) { int a = 3; int b = a++; // a++ 先给b赋值 再自增 // a = a + 1; System.out.println(a); // a = a + 1; int c = ++a; // ++a 先自增 再给c赋值 System.out.println(a); System.out.println(b); System.out.println(c); // 幂运算 使用工具类来操作 double pow = Math.pow(2,3); // 2^3 System.out.println(pow); System.out.println("================"); int i = 1; int j; System.out.println(i); // 1 j = i++; System.out.println(i); // 2 j = i++; System.out.println(i); // 3 j = i++; System.out.println(i); // 4 j = i++; System.out.println(i); // 5 j = i++; System.out.println("i=" + i); // 6 System.out.println(j); // 5 /*System.out.println("================="); i = i++; i = i++; i = i++; i = i++; i = i++; i = i++; // 进操作数栈的始终是 1 ,++是在局部变量表里完成的,赋值操作会覆盖++后的结果。 System.out.println("i=" + i); // i=1*/ /* System.out.println("=================="); int d = 2; int e = 3; int f = d++; // d=3 int g = ++e; // e=4 System.out.println(f); System.out.println(g); System.out.println(d++ + ++e);*/ } }
- 逻辑运算符
public static void main(String[] args) { // 与(&&) 或(||) 非(!) boolean a = true; boolean b = false; // 短路运算 int c = 5; boolean d = (c<4)&&(c++<4); System.out.println(d); System.out.println(c); }
- 位运算符
public static void main(String[] args) { /* A = 0011 1100 B = 0000 1101 // 位运算 跟底层二进制有关 效率极高!!! A&B = 0000 1100 A与B 相与 两对应位都是1 结果为1,否则为0 A|B = 0011 1101 A或B 如果两个A、B对应位都是0 结果为0,否则为1 A^B = 0011 0001 取反(异或) 如果对应位相同为0,否则为1 ~B = 1111 0010 2*8怎么运算最快 计算机不知道2*8=16 可拆分成2*2*2*2 乘2每次在变大 在位运算里变得非常快 << 左移 *2 >> 右移 /2 >>> 右移 0000 0000 0 0000 0001 1 0000 0010 2 0000 0100 4 0000 1000 8 0001 0000 16 */ }
- 字符串运算符
- 在加号两侧,只要由一方有String(字符串)类型(只要不是放最末尾) 他就会把其他的操作数都转换为String再进行连接
- 但是会有运算顺序问题
public static void main(String[] args) { int a = 10; int b = 20; // 字符运算符 在加号两侧,只要由一方有String(字符串)类型(只要不是放最末尾) 他就会把其他的操作数都转换为String再进行连接 // 但是会有运算顺序问题 System.out.println(a + b); // 30 System.out.println("" + a + b); // 1020 直接拼接 System.out.println(a + b + ""); // 30 先相加再拼接 }
- 三元运算符: 返回的数据类型必须相同
public static void main(String[] args) { // 三元运算符 // x ? y : z // 如果x==true 结果为y 否则为z int score = 50; String type = score < 60 ? "不及格" : "及格"; System.out.println(type); }
1.10.包机制 (防止命名空间重复的问题)
- 为了更好地组织类,java提供了包机制,用于区别类名的命名空间。
- 包语句的语法格式:
- package pkg1[. pkg2[. pkg3…]];
- package com.dj.operator;
-
一般利用公司域名倒置作为包名;
- 为了使用某个包的成员,我们需要在java程序中明确导入该包。用 “import” 语句可以完成
- import package1[.package2…].(className | *);
- import com.dj.base.Demo2;
- import com.dj.base.*; // 导入这个包下所有的类
1.11.JavaDoc 加在类上面就是类的注释,加在方法上就是方法的注释
- javadoc命令是用来生成自己API文档的
- 参数信息
- @author 作者名
- @version 版本号
- @since 指明需要最早使用的jdk版本
- @param 参数名
- @return 返回值情况
- throws 异常抛出情况
- 在java所在文件夹下地址栏进入cmd
- 输入命令
- javadoc -encoding UTF-8 -charset UTF-8 Doc.java
((java生成文档 让他的编码为UTF-8编码格式 并且让他的字符集编码 也变成UTF-8) 目的为了多显示一点中文 防止出现乱码)
- javadoc -encoding UTF-8 -charset UTF-8 Doc.java
- 生成的 index.html 即为帮助文档
- 用idea生成Javadoc文档
2.Java流程控制
- 用户交互用Scanner类来获取用户的输入
2.1.Scanner对象
- java提供一个工具类,可以获取用户的输入。java.util.Scanner是Java5的新特征,我们可以通过Scanner类来获取用户的输入。
调用nextInt(), next(), nextLine()等方法是用来做接收作用的,括号里可以填数字,表示要接受数字范围,
- 基本语法:
Scanner scanner = new Scanner(System.in);
- 通过调用Scanner类的 next()(字符串类型) 与 nextLine() 方法获取输入的字符串,在读取前 一般需要用 hasNext() 与 hasNextLine() 判断是否还有输入的数据。
注意:
- 要根据相应方法,来输入指定类型的值,如果输入的数据类型与要求的类型不匹配时,会报异常,程序会终止。
public static void main(String[] args) { // 创建一个扫描器对象,用于接收键盘数据 // 创建Scanner对象,通过System.in接收用户的输入并且把它封装成scanner对象 之后用scanner进行用户的扫描测试 Scanner scanner = new Scanner(System.in); // Scanner实体化 System.out.println("使用next方式接收:"); // 判断用户有没有输入字符串 if (scanner.hasNext()) { // scanner对象是否还有下一个输入数据 String str = scanner.next(); // 创建一个str对象,调用scanner.next()方法 获取指定类型变量 停下来等待接收用户的输入 输入后自动接收 赋值给str变量 程序会等待用户输入完毕 System.out.println("输出的内容为:" + str); // 凡是属于IO流的类如果不关闭会一直占用资源,用完就关养成好习惯 scanner.close(); } }
-
Random类的用法
1.Random类的作用:用来生成随机数
2.Random类的使用步骤:1.导包 2.创建Random类的对象 3.获取随机数 变量类型 变量 = 对象.nextInt() 获取
3.死循环(while 和 for)的应用
public static void main(String[] args) { // 1.1.创建Random类的对象 Random r = new Random(); // 获取(接收)指定范围的随机数(1~100) int num = r.nextInt(100) + 1; // nextInt(100)--> 0~99 加一是整个范围[0+1,99+1] System.out.println("输入你要猜的数(1~100):"); // 创建用户从键盘输入Scanner类的对象 Scanner sc = new Scanner(System.in); // 因为不知道多少次能猜对,所以用死循环改进 // 死循环写法一 while (true){ // 不确定几次猜中,用死循环一直持续下去直到猜中break出 // 接收用户输入的数据 int guessNum = sc.nextInt(); // 比较两数是否相等,给出提示 if (guessNum < num){ System.out.println("猜小了"); }else if (guessNum > num){ System.out.println("猜大了"); }else{ System.out.println("恭喜猜中"); break; // 结束循环 } } // 死循环写法二 /*for (; ; ) { // 存取猜数的对象 int guessNum = sc.nextInt(); if (guessNum < num) { System.out.println("猜小了"); } else if (guessNum > num) { System.out.println("猜大了"); } else { System.out.println("恭喜猜中"); break; // 结束循环 } }*/ }
2.2.顺序结构
- java的基本结构就是顺序结构,除非特别指明,否则就按照顺序从上而下一句一句执行。
- 他是任何一个算法都离不开的一种基本算法结构。
2.3.选择结构
- if 选择结构
public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.println("输入内容:"); String s = scanner.nextLine(); // equals 判断字符串是否一致 if(s.equals("Hello")){ System.out.println(s); } System.out.println("end"); scanner.close(); }
public static void main(String[] args){ Scanner scanner = new Scanner(System.in); System.out.println("输入成绩"); double score = scanner.nextDouble(); if(score == 100){ System.out.println("满分"); }else if (score < 100 && score >= 60){ System.out.println("及格"); }else if(score < 60 && score >= 0){ System.out.println("不及格"); }else{ System.out.println("成绩就不合法"); } scanner.close(); }
- switch结构
jdk7的新特性:表达式的结果可以是字符串!
字符的本质还是数字
反编译 java文件 编译之后 -> 变成class文件(字节码文件) 用反编译工具(IDEA)反编译成java程序或一些东西
char grade = 'C'; // 单个字符 switch (grade){ case 'A': System.out.println("优秀"); break;// 可选 String name = "刘宇"; // jdk7的新特性:表达式的结果可以是字符串! // 字符的本质还是数字 // 反编译 java文件 编译之后 -> 变成class文件(字节码文件) 用反编译工具(IDEA)反编译成java程序或一些东西 switch (name){ case "大刘宇": System.out.println("大刘宇"); break; default: System.out.println("刘宇");
- switch可以识别字符串的原因
2.4.循环结构
- while 和 do while 的区别
- while 先判断后执行
- do…while 先执行后判断; 至少执行一次
int a = 0; while (a < 0){ System.out.println(a); } System.out.println("==========="); do { System.out.println(a); }while (a < 0); while (true){ // 死循环 }
- for循环
for ( ; ; ){ // 死循环 } 打印九九乘法表 // 1.先打出第一列 for (int i = 1; i <= 9; i++){ System.out.print(1 + "*" + i + "=" + (1 * i) + "\t"); } // 2.再用一个循环包起来 // 3.去掉重复项,i == j // 4.调整样式 for (int j = 1; j <= 9; j++) { for (int i = 1; i <= j; i++) { System.out.print(j + "*" + i + "=" + (j * i) + "\t"); } System.out.println(); }
- break 和 continue
- break 终止循环 不再进行
int i = 0; while (i < 100){ i++; System.out.println(i); if (i == 30){ break; } } System.out.println("123");
- continue 跳过本次所有循环内余下的操作,回到下一次是否执行循环判断
int i = 0; while (i < 100){ i++; if (i % 10 == 0){ System.out.println(); continue; // 跳过本次所有循环内余下的操作,回到下一次是否执行循环判断 } System.out.print(i);
- 关于goto 关键字
int count = 0; outer:for (int i = 101; i < 150; i++){ for (int j = 2; j < i/2; j++){ if (i % j == 0){ continue outer; } } System.out.print(i + " "); }
- 打印三角形
// 打印三角形 5行 for (int i = 1; i <= 5; i++){ for (int j = 5; j >= i; j--){ System.out.print(" "); } for (int j = 1; j <= i; j++){ System.out.print("*"); } for (int j = 1; j < i; j++){ System.out.print("*"); } System.out.println(); }
3.方法
3.1.什么是方法(方法的定义)
- public 公共修饰符,所有方法能用
- static 可以通过类本身来调用方法 基本作用:方便在没有创建对象的情况下来进行调用(方法/变量)。
- 方法是类里面的成员函数
System.out.println(); 他是什么意思?
- System是系统的类,out 是System下的输出对象,println(); 是一个方法
- 调用**(System)系统类里面的(out)标准输出对象**中的println(); 方法。
- java方法时语句的集合,他们在一起执行一个功能
- 方法包含于类或对象中
- 方法在程序中被创建,在其他地方被引用
- 设计方法的原则:最好保持方法的原子性,一个方法只完成一个功能。
// main方法 public static void main(String[] args){ /*int sum = add(1,2); System.out.println(sum);*/ test(); } public static void test(){ for (int i = 1; i <= 1000; i++){ if (i%5==0){ System.out.print(i + "\t"); } if (i%(5*3)==0){ System.out.println(); } } } // 加法 public static int add(int a,int b){ return a+b; }
3.2.方法调用
- 调用方法:对象名.方法名(实参列表)
- 当方法返回一个值的时候,方法调用通常被当做一个值,例如:
int larger = max(30, 40);
- 如果方法返回值是void(可以参照main方法),方法调用一定是一条语句。
System.out.println(“hello world”);
- 静态方法和非静态方法
public static void main(String[] args){ // 静态方法 static Student.say(); // 非静态方法 // 实例化这个类 new Student() // 创建了这个类然后,再调用它的方法 // new Student().say1(); // new Student 把这个类实例化,再调用它的方法 // 对象类型 对象名 = 对象值; Student student = new Student(); // 对象值变成实例化对象 student.say1(); } // static是和类一起加载(出现)的,时间片特别早,当class Demo02存在,他就存在了 public static void a(){ // scan(); // 存在的东西调用不存在的东西就会报错 } // 跟对象有关,对象创建/类实例化(通过new)之后才存在 public void scan(){ // 非静态方法实例化后才能被调用 }
- 实参和形参
- 什么是值传递,什么是引用传递。为什么说Java中只有值传递。
3.3.方法的重载
- 方法的重载(overload) loading…
- 定义,在同一个类中,允许存在一个以上的同名方法,只要他们的参数个数或者参数类型不同即可,同名不同参:”两同一不同“:同一个类、相同方法名
参数列表不同,参数个数不同,参数类型不同,参数顺序不同,主要看参数类型- 判断是否重载:跟方法的权限修饰符、返回值类型、形参变量名、方法体都无关!
- 再通过对象调用方法时,如何确定某一个指定的方法:方法名 再到—>参数列表
Demo03 test = new Demo03(); test.getSum(1,2); } // 如下4个方法构成了重载 public void getSum(int i,int j){ System.out.println("1"); // 方法体 } public void getSum(double d1,double d2){ // 参数类型不同 } public void getSum(String s,int j){ } public void getSum(int i,String s){ } /*public int getSum(int i,int j){ return 0; // 非重载 和第一个方法起冲突 第一个是输出值 这个是返回值 }*/ /*public void getSum(int m,int n){ // 与参数名无关 }*/ /*private void getSum(int i, int j){ // 与公共修饰符无关 }*/
- 方法重载练习
public static void main(String[] args){ mOL(3); mOL(3,4); mOL("字符串"); int a = max(2,5); double b = max(3.4,6.5); double c = max(1.2,3.4,4.5); System.out.println(a); System.out.println(b); System.out.println(c); } public static void mOL(int i){ System.out.println("i*i=" + (i * i)); } public static void mOL(int i,int j){ System.out.println("i*j=" + (i * j)); } public static void mOL(String s){ System.out.println(s); } public static int max(int i,int j){ return i > j ? i : j; } public static double max(double i,double j){ return i > j ? i : j; } public static double max(double m,double n,double p){ double max = m > n ? m : n; return max > p ? max : p; }
3.4.命令行传递参数
- 命令行里编译java文件和运行class文件一定要找到放class文件的pakage的路径,否则运行失败
- IDEA里点击terminal即可打开命令行
3.5.可变参数
- jdk1.5开始,java支持传递同类型的可变参数给一个方法。
- 在方法声明中,在指定参数类型后加一个省略号(…)
- 一个方法只能指定一个可变参数,他必须是方法的最后一个参数。任何普通的参数必须在他之前声明。
public static void main(String[] args){ Demo05 demo05 = new Demo05(); demo05.test(1,2,3,4,5); } public void test(int x,int...i){ System.out.println(x[0]); // 1 System.out.println(i[0]); // 2 System.out.println(i[1]); // 3 System.out.println(i[2]); // 4 System.out.println(i[3]); // 5 }
3.6.递归
public static void main(String[] args){ System.out.println(f(5)); } public static int f(int n){ if (n == 1){ System.out.println(1); return 1; }else{ System.out.print(n + "*" + "f" + "("+(n-1)+")"+"*"); return n * f(n-1); } }
3.7.数组的定义
一维数组: int[] x 或者 int x[] 二维数组: int[][] y 或者 int[] y[] 或 int y[][]
一.数组的概述
- 1.数组的理解,数组(Array), 是多个相同类型数据按一定顺序排列的集合,并使用一个名字命名,并通过编号的方式对这些数据进行统一管理
- 2.数组相关的概念:
- 数组名
- 元素
- 角标,下标,索引
- 数组长度(元素个数)
- 3.数组的提点:
- 素组是有序排列的
- 数组属于引用数据类型的变量. 数组的元素,既可以是基本数据类型,也可以是引用数据类型
- 创建数组对象会在内存中开辟一整块连续的空间
- 数组的长度一旦确定, 就不能修改
-
- 数组的分类:
- 按照维数: 一维数组, 二维数组 …
- 按照数组元素的类型: 基本数据类型元素的数组, 引用数据类型袁术的数组
3.8.数组声明创建
- 只要不是和数据库交互相关的功能,数组的下标/角标/索引,都是从0开始的
3.9.数组的四个基本特点
3.10.内存分析
-
数组本身就是对象,Java中对象是在堆 (heap) 中的, new关键字的东西都会在堆里面
- 栈里面主要存放局部变量,在方法中定义的变量都叫局部变量,堆中存放的数据叫new出来的结构:典型(对象,数组)
- 声明数组
- 内存的简化结构
int[ ] array = null; 声明时候数组并不存在 只有一个名字 只在栈中不在堆中,堆还是空的
- 创建数组
- array = new int [10]; 数组创建在了堆中 在堆里面分配了一片连续的内存空间 定义了大小,这个空间就是array的名字 空间被分成10个小份元素,都是int类型,堆里就有了int对象
- 堆里面的连续的元素内存空间会有首地址,首地址值通常用十六进制数表示,然后把地址值赋给栈空间的array,栈空间的array通过地址值就能**(调用)**找到堆空间的创建的数组,他俩就联系起来了
- 给数组赋值
array[0] = 1; …
创建完的数组中,每个元素都有初始化值,数组元素数字、字符类型默认值为0,字符串(String)类型为null,用角标的方式去调用数组中指定元素然后手动赋值覆盖掉原值
- 当数组下标超出定义个数时 编译器报错
ArrayIndexOutOfBoundsException 数组下标越界
3.11.三种初始化,数组元素的默认初始化值
String[] arr6 = new String[]{"Tom","Jerry","Jim"}; // 静态初始化
数组元素的默认初始化值:
整型是:0
浮点型是:0.0
char(字符)型是:数字 0 或 ‘\u0000’,而非 字符 ‘0’
Boolean型是 false
String(字符串)类型是 null
后面面向对象中的类的属性的默认初始化值也是一样的
3.12.数组边界
public static void main(String[] args){ // 静态初始化 int[] arr = new int[]{1,2,3,4,5}; System.out.println(arr[0]); System.out.println("=========="); // 动态初始化 String[] arr1 = new String[4]; // 用数组元素角标调用数组,给元素赋值 arr1[1] = "李华"; arr1[2] = "张三"; System.out.println(arr1[0]); System.out.println(arr1[1]); // 数组存放在堆中的内存空间的首地址发生改变 // 以后见到new 堆空间就要重新开辟 重新给arr1动态初始化 // arr1变量已经指向新的数组,旧的数组将被垃圾回收机制在不定时间回收,运用引用计数算法 // 当main方法执行完毕,arr1先出栈,新的数组变成垃圾被回收,最后arr出栈的数组也被垃圾 // string 是放在常量池里的 arr1 = new String[3]; System.out.println(arr1[2]); }
- 一维数组应用
public static void main(String[] args){ System.out.println("请输入学生人数:"); Scanner scanner = new Scanner(System.in); // 存入学生人数 int number = scanner.nextInt(); // new一个学成绩数组,存入学生成绩 int[] scores = new int[number]; System.out.println("输入" + number + "个学生成绩"); // 给数组元素赋值 for (int i = 0; i < scores.length; i++){ scores[i] = scanner.nextInt(); } // 获取数组元素的最大值,最高分 int maxScore = 0; for (int i = 0; i < scores.length; i++){ if (maxScore < scores[i]){ maxScore = scores[i]; } } // 根据每个学生成绩的和最高分的差值,得到成绩的等级,并输出分数和成绩 char level; for (int i = 0; i < scores.length; i++){ if (maxScore - scores[i] <= 10){ level = 'A'; }else if (maxScore - scores[i] <= 20){ level = 'B'; }else if (maxScore - scores[i] <= 30){ level = 'C'; }else{ level = 'D'; } System.out.println("第" + i + "个学生成绩的等级是" + level + "成绩是" + scores[i]); } }
- 优化后
public static void main(String[] args){ System.out.println("请输入学生人数:"); Scanner scanner = new Scanner(System.in); int number = scanner.nextInt(); // new 一个学生成绩数组 存入 int[] scores = new int[number]; System.out.println("请输入" + number + "个学生成绩"); int maxScore = 0; for (int i = 0; i < scores.length; i++){ scores[i] = scanner.nextInt(); if (maxScore < scores[i]){ maxScore = scores[i]; } } char level; for (int i = 0; i < scores.length; i++){ if (maxScore - scores[i] <= 10){ level = 'A'; }else if (maxScore - scores[i] <= 20){ level = 'B'; }else if (maxScore - scores[i] <= 30){ level = 'C'; }else{ level = 'D'; } System.out.println("第" + (i+1) + "个学生的成绩等级 " + level + " 成绩是 " + scores[i]); } }
- 反转数组
public static void main(String[] args){ int[] arrs = {1,2,3,4,5}; // 后面arrays代表一个数组,前面的变量代表数组里的每一个元素 // 增强型的for循环,jdk1.5以上版本特性,没有下标,适合打印输出 /*for (int array : arrays){ // arrays代表数组,array代表数组元素 System.out.println(array); }*/ int[] reverse = reverse(arrs); // 把反转的数组返回一个结果,这结果是一个数组 printArray(reverse); // 实参 } public static int[] reverse(int[] arrays){ // int[] 返回int数组类型 int[] result = new int[arrays.length]; // 创建一个结果数组存放要传入的数组 for (int i = 0,j = arrays.length-1; i < arrays.length; i++,j--){ result[j] = arrays[i]; } return result; // 返回这个方法的结果数组 } public static void printArray(int[] arrays){ // 形参 for (int i = 0; i < arrays.length; i++){ System.out.print(arrays[i]+" "); } }
- 反向遍历数组
public static void main(String[] args){ int[] arrs = new int[]{1,2,3,4,5}; reverse(arrs); } public static void reverse(int[] arrays){ for (int i = arrays.length - 1; i >= 0; i--){ System.out.println(arrays[i]); } }
3.13.多维数组
- 二维数组的使用
- 规定: 二维数组分为外层数组元素,内层数组的元素
int[][] arr = new int[4][3];
外层元素: arr[0],arr[1]等
内层元素: arr[0][0], arr[1][2]等- 二维数组的默认初始化值
- 针对于初始化方式一,例如: int[][] arr = new int[4][3];
外层元素初始化值为: 地址值
内层元素初始化值为: 与一维数组初始化值相同- 针对初始化方式er" 比如: int[][] arr = new int[4][];
外层元素初始化值为: null
内层元素初始化值为: 不能调用,空指针异常,否则报错
// 二维数组声明和初始化 // 静态初始化 int[][] arr1 = new int[][]{{1,2,3},{4,5},{6,7,8}}; // 动态初始化1 String[][] arr2 = new String[3][2]; // 动态初始化2 String[][] arr3 = new String[3][]; // 这样写也正确: int[] arr4[] = new int[][]{{1,2,3},{4,5},{6,7,8}}; int[] arr5[] = {{1,2,3},{4,5},{6,7,8}}; // 错误写法: // String[][] arr6 = new String[][4]; // String[4][3] arr7 = new String[][]; // int[][] arr8 = new int[4][3]{{1,2,3},{4,5},{6,7,8}};
// [4][2] 四行两列 /* 1,2 arr[0] 3,4 arr[1] 5,6 arr[2] 7,8 arr[3]*/ // 二维数组的元素是一维数组 public static void main(String[] args){ int[][] arr = new int[][]{{1,2},{3,4},{5,6},{7,8}}; printArray(arr[0]); // 打印第一行元素,1 2 System.out.println(); System.out.println(arr[0][1]); // 打印第一行第二列元素, 2 System.out.println(arr.length); // 打印数组长度,也就是一维数组行数,4 System.out.println(arr[0].length); // 第一行一维数组的元素个数,2 System.out.println("=========="); // 二维数组遍历 for (int i = 0; i < arr.length; i++){ for (int j = 0; j < arr[i].length; j++){ System.out.println(arr[i][j]); } } } public static void printArray(int[] arrays){ for (int i = 0; i < arrays.length; i++){ System.out.println(arrays[i]); } }
- forEach 增强for循环
int[][] arrays = new int[][]{{1, 2}, {3, 4}, {5, 6}}; for (int[] array : arrays) { for (int element : array){ System.out.print(element+" "); } }
- 二维数组内存解析
- 要注意的是: 这里的十六进制的地址值并不是真实的在内存当中真实的地址他和c语言的地址值是有区别的,这里的地址值是Java虚拟机虚拟算出来的一个哈希值,他相当于屏蔽了底层真实内存的地址,不需要再去真实的地址中去往后移动几位是谁是谁,比c的指针简单
int[][] arr1 = new int[4][];
1.首先在栈中,main方法中声明了一个局部变量 arr1,接着只要是new的结构都是在堆空间中开辟,在堆中,new了一个int型的长度是4的数组,这个数组会有首地址值.通过地址值,栈空间的变量arr1就可指向堆空间中造好的数组,这时候,二维数组列的位置没标个数,意味着数组里的每一行都没初始化过,所以此时,二维数组每行的元素初始化值是多少就取决于元素的类型,每行元素都是int型的一维数组(int[ ])类型,因为每个元素都是一维的合一起才叫二维数组,数组是引用类型且没赋过值,初始值就是 null
arr1[1] = new int[]{1,2,3};
2.通过变量arr1,找到第二个元素并给他初始化 new 了一个结构在堆空间中,长度是3的数组,紧接着,元素中的数组会有首地址值,把地址值赋给了arr1[1],因此就能指向堆空间中新造的数组了,新数组的元素是int型,默认初始化为0,然后被分别赋给了1,2,3
arr1[2] = new int[4]
3.找到arr1第三个元素,在堆空间中新造一个长度4的数组,该新数组会有首地址值,并把地址值给到arr1[2]元素,通过地址值,就能找到堆空间中新开辟的一维数组,该数组元素值动态初始化为0
arr1[2][1] = 30;
4.将arr1[2][1]赋值为30
5.当第四步结束后,如果在main方法大括号中定义的,就出去了,出去后,在方法里边栈空间中定义的arr1就要弹出栈,因为超出作用域就要出栈,变量arr1出栈指针就没了,堆空间中的结构发现栈空间中没指针指向,该结构就要被回收,该结构被回收,它的两个元素没有指针指向,最后两元素也被回收,最后整体结构都没了
- 练习
- 多维数组的使用
int[] x,y[];
等同于 int[] x, int[] y[];
- 变量x的类型: int型的一维数组类型或一维数组元素是int型的类型(x是指向一维数组的引用)
- 变量y的类型: int型的二维数组
赋值原则:1.同类型(什么类型就赋给什么类型) 2.满足自动类型提升 例如:int x = 1; double y = x;
- a: no int型的数 != 二维数组类型
- b: yes 二维数组的元素 = 一维数组类型
- c: no int型的数 != 一维数组类型
- d: no
- e: yes int型的数 = int型的数
- f: no 一维数组类型 ≠ 二维数组类型 (类型不同,就算同是地址也不能赋值)
3.14.Arrays类
public static void main(String[] args){ int[] a = {115,6665,15,4633,452}; System.out.println(a); // 数组地址 // 打印数组元素 System.out.println(Arrays.toString(a)); // 转换成字符串 // printArray(a); Arrays.sort(a); // 数组进行排序:升序 System.out.println(Arrays.toString(a)); // a - 要填充的数组 // val - 要存储在数组所有元素的值 /* Arrays.fill(a,10); // 给数组填充赋值 System.out.println(Arrays.toString(a));*/ Arrays.fill(a,1,3,0); // 左闭右开区间 之间的元素被填充 System.out.println(Arrays.toString(a)); } public static void printArray(int[] arr){ for (int i = 0; i < arr.length; i++){ if (i == 0){ System.out.print("["); } if (i == arr.length - 1){ System.out.print(arr[i] + "]"); }else{ System.out.print(arr[i] + ", "); } } }
- 数组算法题
1.数组元素的赋值:
- 杨辉三角:
public static void main(String[] args) { System.out.println("输入杨辉三角的行数:"); Scanner scan = new Scanner(System.in); // 创建键盘输入数据的对象 int num = scan.nextInt(); // 接收键盘输入的数据 int[][] yangHui = new int[num][]; // 给每行数组赋值 for (int i= 0;i < yangHui.length;i++){ yangHui[i] = new int[i+1]; // 每行创建数组元素对象 yangHui[i][0] = yangHui[i][i] = 1; // 数组每行元素第一个和最后一个赋值为1 // 从第二行开始赋值 if (i > 1){ for (int j = 1;j < yangHui[i].length - 1;j++){ yangHui[i][j] = yangHui[i-1][j] + yangHui[i-1][j-1]; } } } // 遍历杨辉三角 for (int i = 0;i < yangHui.length;i++){ for (int j = 0;j < yangHui[i].length;j++){ System.out.print(yangHui[i][j] + "\t"); } System.out.println(); } scan.close(); }
2.求数值型数组中元素的最大值,最小值,平均数,总和等
public static void main(String[] args) { int[] arr = new int[10]; for (int i = 0;i < arr.length;i++){ arr[i] = (int) (Math.random() * (99 - 10 + 1) + 10); } // 遍历数组 for (int i = 0; i < arr.length; i++){ System.out.print(arr[i] + "\t"); } System.out.println(); // 求数组最大值 int maxValue = arr[0]; // 假如等于0 当数组为负数时,0就不适用了 for (int i = 1;i < arr.length;i++){ if (maxValue < arr[i]){ maxValue = arr[i]; } } System.out.println("最大值为:" + maxValue); // 求数组最小值 int minValue = arr[0]; for (int i = 1; i < arr.length; i++){ if (minValue > arr[i]){ minValue = arr[i]; } } System.out.println("最小值为:" + minValue); //求数组总和 int sum = 0; for (int i = 0; i < arr.length; i++){ sum = sum + arr[i]; } System.out.println("总和为:" + sum); // 求数组平均数 int avgValue = sum / arr.length; System.out.println("平均数为:" + avgValue); }
3.数组的复制,反转,查找(线性查找,二分查找)
- 数组的赋值: 相当于创建快捷方式(两个数组地址值相同)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J5RzPgkF-1630720147806)(https://upload-images.jianshu.io/upload_images/24940810-3a94f134b0e57553.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]
- 数组的复制: 相当于新复制一个文件夹,两数组互不影响(地址值不同)
public static void main(String[] args) { String[] arr = new String[]{"aa","bb","gg","hh","dd"}; // 数组的复制(区别于数组的赋值: arr1 = arr) String[] arr1 = new String[arr.length]; for (int i = 0; i < arr1.length;i++){ arr1[i] = arr[i]; } // 数组的反转 // 方式一: // 交换长度如果不取一半,一半往后又继续交换回来了 /*for (int i = 0; i < arr.length / 2;i++){ String temp = arr[i]; arr[i] = arr[arr.length - i - 1]; arr[arr.length - i - 1] = temp; }*/ // 方式二: // 循环内定义一个头部一个尾部参数 /*for (int i = 0,j = arr.length - 1; i < j; i++,j--){ String temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; }*/ // 遍历 for (int i = 0;i < arr.length; i++){ System.out.print(arr[i] + "\t"); } System.out.println(); // 查找(或搜索) // 线性查找: String dest = "dd"; dest = "cc"; boolean isFlag = true; // 新定义一个标识为真 for (int i = 0; i < arr.length; i++){ // 字符串比较用 变量.equals(比较内容) int型比较直接用 == 就行 if (dest.equals(arr[i])){ System.out.println("找到了指定元素,位置为:" + i); isFlag = false; break; // 找到就退出循环,提高运行效率 } } if (isFlag){ System.out.println("没找到"); // 不能在for循环里写,没遍历完不能说没找到 } // 二分法查找: // 前提: 所要查找的数组必须有序 int[] arr2 = new int[]{-95,-35,-13,0,5,14,65,223,651}; int key = 5; int low = 0; // 初始的首索引 int high = arr2.length - 1; // 初始的尾索引 boolean isFlag1 = true; // 定义一个标识做判断是否进入if条件 while (low <= high){ int middle = (low + high) / 2; // middle如果放在while循环外 mddle永远是5(数组长度的一半那个值),所以不能放在外面 if (key == arr2[middle]){ System.out.println("找到了,索引为:" + middle); isFlag1 = false; break; // 找到后退出while循环 }else if (key < arr2[middle]){ high = middle - 1; }else{ // key > arr2[middle] low = middle + 1; } } if (isFlag1){ System.out.println("没找到"); } }
4.数组元素的排序算法
- 冒泡排序算法
public static void main(String[] args){ int[] arr = new int[]{43,32,76,-98,0,64,33,-21,32,99}; int[] sort = sort(arr); System.out.println(Arrays.toString(sort)); } public static int[] sort(int[] arrays){ int temp = 0; // 循环的轮数 for (int i = 0; i < arrays.length - 1; i++){ // 每两个数比较的次数 for (int j = 0; j < arrays.length - 1 - i; j++){ if (arrays[j] > arrays[j+i]){ temp = arrays[j]; arrays[j] = arrays[j+1]; arrays[j+i] = temp; } } } return arrays; }
3.15.稀疏数组
public static void main(String[] args) { // 创建一个二维数组 11*11 0:没有棋子 1:黑棋 2:白棋 int[][] array1 = new int[11][11]; array1[1][2] = 1; array1[2][3] = 2; // 输出原始数组 for (int[] arr1 : array1) { for (int element : arr1) { System.out.print(element+"\t"); } System.out.println(); } System.out.println("=================="); // 转换为稀疏数组保存 // 获取有效值的个数 int sum = 0; for (int i = 0;i < 11;i++){ for (int j = 0;j < 11;j++){ if (array1[i][j] != 0){ sum++; } } } System.out.println("有效值个数:" + sum); System.out.println("============="); //创建一个稀疏数组的数组 int[][] array2 = new int[sum+1][3]; // 稀疏数组的栏 array2[0][0] = 11; // 稀疏数组存的行数 array2[0][1] = 11; // 数组存的列数 array2[0][2] = sum; // 有效值的个数 // 遍历二维数组,将非零的值,存放在稀疏数组中 int count = 0; for (int i = 0;i < array1.length;i++){ for (int j = 0;j < array1[i].length;j++){ if (array1[i][j] !=0){ count++; // 计数作用 array2[count][0] = i; array2[count][1] = j; array2[count][2] = array1[i][j]; } } } // 输出稀疏数组 System.out.println("稀疏数组"); for (int i = 0;i < array2.length;i++){ System.out.println(array2[i][0]+"\t"+array2[i][1]+"\t"+array2[i][2]+"\t"); } System.out.println("==============="); // 读取稀疏数组 int[][] array3 = new int[array2[0][0]][array2[0][1]]; // 读取稀疏数组的行和列 // 给其中的元素还原它的值 for (int i = 1;i < array2.length;i++){ // i从1开始遍历,因为第0行是稀疏数组的头部信息 array3[array2[i][0]][array2[i][1]] = array2[i][2]; } System.out.println("还原数组:"); for (int i = 0;i < array3.length;i++){ for (int j = 0;j < array3[i].length;j++){ System.out.print(array3[i][j]+"\t"); } System.out.println(); } }
- 第二种写法
// 创建一个二维数组 int[][] array1 = new int[5][6]; array1[1][3] = 2; array1[4][2] = 6; for (int i = 0;i < array1.length;i++){ for (int j = 0;j < array1[i].length;j++){ System.out.print(array1[i][j]+"\t"); } System.out.println(); } // 获取有效值 转换为稀疏数组 int sum = 0; for (int i = 0;i < array1.length;i++){ for (int j = 0;j < array1[i].length;j++){ if (array1[i][j] != 0){ sum++; } } } System.out.println("有效值:"+sum); System.out.println("================"); // 创建稀疏数组 int[][] array2 = new int[sum+1][3]; array2[0][0] = 5; // 第一行第一列 array2[0][1] = 6; // 第一行第二列 array2[0][2] = sum; int count = 0; for (int i = 0;i < array1.length;i++){ for (int j = 0;j < array1[i].length;j++){ if (array1[i][j] != 0){ count++; array2[count][0] = i; array2[count][1] = j; array2[count][2] = array1[i][j]; } } } System.out.println("稀疏数组:"); for (int i = 0;i < array2.length;i++){ // 将稀疏数组 System.out.println(array2[i][0] + "\t" + array2[i][1] + "\t" + array2[i][2] + "\t"); } // 读取稀疏数组 int[][] array3 = new int[array2[0][0]][array2[0][1]]; for (int i = 1;i < array2.length;i++){ array3[array2[i][0]][array2[i][1]] = array2[i][2]; } System.out.println("输出还原数组:"); for (int i = 0;i < array3.length;i++){ for (int j = 0;j < array3[i].length;j++){ System.out.print(array3[i][j]+"\t"); } System.out.println(); }
4.面向对象
- 属性+方法变成一个类
Java面向对象学习三条主线:
1.Java类及类的成员:属性,方法,构造器;代码块,内部类
2.面向对象的三大特征:封装性,继承性,多态性,(抽象性)
3.其他关键字:this,super,static,final,abstract,interface,package,import
- 面向对象编程思想?
(类,对象; 面向对象的三大特征;…) - 谈谈你对面向对象中类和对象的理解,并指出二者关系?
类: 是对一类事物的描述, 抽象的, 概念上的内容
对象: 实实在在存在的一个个体. new出来的东西,在内存当中存在的, 在内存中真正的创建了一个对象,占据了内存一定空间
对象: 是由类派生(new)出来的
4.1.什么是面向对象
-
面向对象编程(Objected-Oriented-Programming,OOP),Java的核心思想
-
面向对象编程的本质是: 以类的方式组织代码,以对象的形式组织(封装)数据.
-
抽象 把相似的抽取出来
-
三大特性:
- 封装 把一个东西打包封装,留一个口方便把东西拿出
- 继承 子类可以继承父类的所有东西,多个子类继承的东西是一样的
- 多态 同一个事物会有多种形态
-
从认识论角度考虑是先有对象后有类. 对象,是具体的事物. 类,是抽象的, 是对对象的抽象
-
从代码运行角度考虑是先有类后有对象. 类是对象的模板.
-
值传递
// 值传递 public static void main(String[] args){ int i = 1; System.out.println(i); Demo04.change(i); System.out.println(i); i = Demo04.change1(i); System.out.println(i); } // 返回值为空 public static void change(int a){ a = 10; } // 返回值不为空 public static int change1(int a){ a = 10; return a; }
- 引用传递
public class Demo05 { // 一个类可以有多个public class 但是可以有多个class // 引用传递: 对象,本质还是值传递 public static void main(String[] args) { // Person是一个引用,指向的是堆里面的对象 Person person = new Person(); // 实例化Person对象 System.out.println(person.name); // null // Demo05.change(person); change(person); // 引用传递传递的是对象的地址 System.out.println(person.name);// dijia // 变量名对应的内存地址不一样 // 值传递,传递后的值被改了不影响原变量 // 因此只是将方法中的变量指向了字符串,并未改变main方法原变量的引用 } public static void change(Person person) { // person是一个对象:指向Person这个类 // 或是 Person person = new Person();这是个具体的人,可以改变属性! person.name = "dijia"; } } // 定义一个Person类,有一个属性: name class Person { String name; }
4.2.创建与初始化对象
- 面向对象的两个要素:
- 类:对一类事物的描述,是抽象的,概念上的定义
- 对象:是实际存在的该类事物的每个个体,因而也称为实例(instance)
- 类和对象的创建
面向对象思想的体现一 ,类和对象的创建和执行操作(面向对象思想落地地实现):
1.创建类,设计类的成员
2.创建类的对象
3.通过"对象.属性"或"对象.方法()"调用对象的结构
二,如果创建了一个类的多个对象,每个对象都独立的拥有一套类的属性(无static关键字的)
意味着,如果我们修改一个对象的属性a,不影响另一个属性a的值.(static为可共享属性)
// 测试类 public class PersonTest { // main方法作为程序入口 public static void main(String[] args) { // 创建Person类的对象=类的实例化=实例化类 Person p1 = new Person(); // 类是对象的类型,对象是类的实例,Person是引用的变量类型 // 调用对象的(功能和行为)结构: 属性和方法 // 调用属性: "对象.属性;" p1.name = "tom"; p1.isMale = true; System.out.println(p1.name); // 调用方法: "对象.方法();" p1.eat(); p1.sleep(); p1.talk("Chinese"); System.out.println("================"); Person p2 = new Person(); System.out.println(p2.name); // null System.out.println(p2.isMale); // false System.out.println("==================="); // Person p3 = p1; // p1对象的地址值赋给了p3,导致p1和p3指向了堆空间中的同一个对象实体 System.out.println(p3.name); // tom p3.age = 10; System.out.println(p1); System.out.println(p3); System.out.println(p1.age); // 10 } } // 创建类,设计类的成员:包括成员方法,成员变量 class Person{ // 属性= 成员变量= 域,字段 String name; int age = 1; boolean isMale; // 设计方法:行为 public void eat(){ System.out.println("人可以吃饭"); } public void sleep(){ System.out.println("人可以睡觉"); } public void talk(String language){ System.out.println("人可以说话使用:" + language); }
- 对象的内存解析:
- 栈的特点:先进后出
1.new Person(); 先造了一个对象,new的结构都在堆里,意味着在堆空间中要造一个对象的实体,该对象会有个地址值称作首地址值; 在栈空间中声明了一个变量p1,此时的p1实际是定义在main方法中的,方法中定义的变量都是局部变量,此时,new结构的数据赋值给p1,其实赋过来的是个地址,通过赋值地址值后,栈空间中的p1就指向了堆空间中造的对象实体,类在设计的时候声明过三个属性,这些属性不同于局部变量,属性存在于堆空间中,在造好的对象里面,在赋值过程中会给定默认的初始化值和先前赋好的值
2.通过p1找到new的结构调用name把null改成tom,tom等赋值后的数据实际在方法区中的字符串常量池里
3.重新在堆空间中造一个对象实体, 该实体有一个首地址值,将这个地址值赋值给栈空间中现在加载的变量p2,栈空间中的p2对象就可以指向堆空间的这个对象实体,对象实体也独立生成一份默认初始化值和先有的值
4.又在main方法(栈空间)中声明了一个p3局部变量,p3拿p1赋的值,p1存的是地址值,所以传给p3也是相同地址值,通过p1传过来的地址值,p3也顺着地址指向p1的在堆空间中的结构,所以p3不能叫新创建的一个对象,只能算新声明的一个变量,此时p1和p3指向了堆空间中的同一个对象实体
4.3.属性与局部变量的对比
类中属性的使用
属性(成员变量) vs 局部变量
- 1.相同点:
- 1.1.定义变量的格式: 数据类型 变量名 = 变量值
- 1.2.先声明,后使用
- 1.3.变量都有其对应的作用域
- 2.不同点:
- 2.1.在类中声明的位置不同
属性:直接定义在类的一对{}里
局部变量:声明在方法内,方法形参,代码块内,构造器形参,构造器内部的变量 - 2.2.关于权限修饰符的不同
属性:可以在声明属性时,指明其权限,使用权限修饰符.
常用的权限修饰符: private,public,缺省(相当于默认,default),protected —>类的封装性
目前,声明属性时,使用缺省就行了
局部变量:不可以使用权限修饰符,可理解为其权限被方法的权限代替了 - 2.3.默认初始化值的情况:
属性: 类的属性,根据其类型,都有默认初始化值.
整型(byte,short,int,long): 0
浮点型(float,double): 0.0
字符型(char), 0 (或’\u0000’)
布尔型(boolean): false
引用数据类型:(类(特殊的String),数组,接口): null
局部变量: 没有初始化值,意味着调用局部变量之前,一定要显示赋值
特别地: 形参在调用时,赋值即可. - 2.4.二者在内存中加载的位置:
(非static)属性:加载到堆空间中(static属性都放在方法区中)
局部变量:加载到栈空间中
- 2.1.在类中声明的位置不同
public static void main(String[] args) { User u1 = new User(); System.out.println(u1.name); System.out.println(u1.age); System.out.println(u1.isMale); u1.talk("中文"); u1.eat(); } } class User{ // 属性(成员变量) String name; // private权限小,出了定义的这个类就不能调了 public int age; // public权限大,在外的就能调 boolean isMale; public void talk(String language){ // language:形参(局部变量) System.out.println("我们使用" + language + "交流"); } public void eat(){ String food = "烙饼"; // (声明)定义在方法内的变量叫局部变量 System.out.println("北方人吃" + food); }
4.4.空指针异常(NullPointerException)
关于垃圾回收器: GC
在Java语言中,垃圾回收器主要针对的是堆内存.
当一个Java对象没有任何引用指向该对象的时候,
GC会考虑将该垃圾数据释放回收掉.
- 出现空指针异常的前提条件是?
- "空引用"访问实例(对象相关,例如id)相关的数据时,都会出现呢空指针异常.
/* 空指针异常 */ public class NullPointerTest { public static void main(String[] args) { // 创建客户对象 Customer1 c1 = new Customer1(); // 访问这个客户对象 System.out.println(c1.id); // 重新给id赋值 c1.id = 242; System.out.println(c1.id); c1 = null; // NullPointerException 空指针异常 // 编译器没问题,因为编译器只检查语法,编译器发现c是Customer类型, // Customer类型中由id属性,所以可以: 调用c.id,语法过了, // 但是运行的时候需要对象的存在,但是对象丢失,就只能出现异常 System.out.println(c1.id); } } // 客户类 class Customer1{ // 客户id属性 int id; // 成员变量中的实例变量,应该先创建对象,再通过"引用."的方式访问 }
4.5.对象数组
public class StudentTest1 { public static void main(String[] args) { // 声明自定义类Student类型的对象数组 Student1[] stus = new Student1[20]; // 类似于String[] arr = new String[]; for (int i = 0;i < stus.length;i++){ // 给数组元素赋值 stus[i] = new Student1(); // 给Student对象的属性赋值 stus[i].number = (i + 1); // 年级: [1,6] // 随机数a~b公式: int value = (int)(Math.random() * (b - a + 1) - a) stus[i].state = (int)(Math.random() * (6 - 1 + 1) + 1); // 分数: [0~100] stus[i].score = (int)(Math.random() * (100 - 0 + 1)); } // 想在main方法里调用其他方法,要在main方法公共类里造一个当前类的对象, StudentTest1 test = new StudentTest1(); // 遍历学生数组 test.print(stus); /*for (int i = 0;i < stus.length;i++){ // stus[i].info(); System.out.println(stus[i].info2()); }*/ System.out.println("======================"); // 打印三年级(state值为3)的学生信息 test.searchState(stus,3); /*for (int i = 0;i < stus.length;i++){ if (stus[i].state == 3){ stus[i].info(); } }*/ System.out.println("========================"); // 使用冒泡排序按学生成绩排序,并遍历所有学生信息 /* for (int i = 0;i < stus.length - 1;i++){ for (int j = 0;j < stus.length - 1 - i;j++){ if (stus[j].score > stus[j+1].score){ // 这里要交换数组元素的对象!意思是把整个人交换 // 如果只交换成绩,学号和年级都没换,相当于"作弊" Student1 temp = stus[j]; stus[j] = stus[j+1]; stus[j+1] = temp; } } }*/ /*for (int i = 0;i < stus.length;i++){ System.out.println("学号:" + stus[i].number + "年级:" + stus[i].state + "成绩:"+ stus[i].score); }*/ } // 遍历Student1[]类型数组的操作 /** * @Description 遍历Student1[]类型数组的操作 * @author tiga * @date time * @param stus */ public void print(Student1[] stus){ // 传入Student1[]类型数组的形参 for (int i = 0;i < stus.length;i++){ stus[i].info(); } } // 查找某年级的学生信息 /** * * @Description 查找Student类型数组中指定年级的学生信息 * @author tiga * @date time * @param stus 要查找的数组 * @param state 要查找的年级 */ public void searchState(Student1[] stus,int state){ // 传入要找的那个数组和要找的年级为形参 for (int i = 0;i < stus.length;i++){ if (stus[i].state == state){ System.out.println(stus[i].info2()); } } } // 冒泡排序学生成绩,遍历学生信息 public void sort(Student[] stus){ for (int i = 0;i < stus.length - 1;i++){ for (int j = 0;j < stus.length - 1 - i;j++){ if (stus[j].score > stus[j+1].score){ Student temp = stus[j]; stus[j] = stus[j+1]; stus[j+1] = temp; } } } } } class Student1{ int number; // 学号 int state; // 年级 int score; // 成绩 public void info(){ System.out.println("学号:" + number + "年级:" + state + "成绩:" + score); } public String info2(){ return "学号:" + number + "年级:" + state + "成绩:" + score; } }
- 对象数组的内存解析
引用类型变量只能存储null或对象的地址值(含变量的类型).
4.6.匿名对象
一.理解"万事万物皆对象"
1.在Java语言范畴中,都将功能,结构等封装到类中,通过类的实例化,来调用具体的功能结构
Scanner,String
文件:File
网络资源: URL
2.涉及到Java语言与前端HTML,后端的数据库交互时,前后端的结构在Java层面交互时,都体现为类,对象
二.内存解析的说明:
1.引用类型的变量,只可能存储两类值:null 或 地址值(含变量的类型)
三.匿名对象的使用
1.理解:创建的对象,没有显式的赋给一个变量名.即为匿名对象
2.特征: 匿名对象只能调用一次
3.使用: 可以将匿名对象放入方法里多次调用
- 内存简单分析
在调用方法里new完Phone这个类的对象,在堆里产生的空间也会有一个地址值,调用时new的对象其实是赋值给了定义类里的方法里作为形参,形参又是个局部变量,且该变量会存入栈中,相当于把new Phone这个类出来的对象的地址值赋给了形参,只要是赋给了变量,对象就可以多次使用,从而形参就指向了堆中new的新对象, 就能通过新对象的地址调用其他方法, 这时调用的是同一个匿名对象,这相当于把匿名的对象赋给了有名的对象(形参).
public class InstanceTest { public static void main(String[] args) { Phone p = new Phone(); // p就是Phone对象的变量名 // p = null; System.out.println(p); p.sentEmail(); p.playGame(); // 匿名对象 // 两者调用的不是同一对象 new Phone().sentEmail(); new Phone().playGame(); new Phone().price = 2000; new Phone().showPrice(); // 0.0 System.out.println("======================"); // 匿名对象的使用 PhoneMall mall = new PhoneMall(); mall.show(new Phone()); // 理论上是匿名 ShowAddress add = new ShowAddress(); add.printAdd(new PhoneMall()); } } class ShowAddress{ public void printAdd(PhoneMall phone){ System.out.println(phone); } } class PhoneMall{ public void show(Phone phone){ // 实际上形参就是匿名函数的名字 phone.sentEmail(); phone.playGame(); } } class Phone{ double price; // 价格 public void showPrice(){ System.out.println("手机价格是:" + price); } public void sentEmail(){ System.out.println("发送邮件"); } public void playGame(){ System.out.println("玩游戏"); } }
- 理解变量的赋值
- 当在方法中定义了基本数据类型的局部变量,其变量存放在栈中,给变量赋值的数字就是变量在栈内存中存放的东西, 变量之间的赋值传递, 也是传递数字.
int m = 10; int n = m; // n == 10
- 当在方法中定义了引用类型的局部变量,其变量也是存放在栈中,但是变量赋给的值的地址值, 所以当同类型变量之间赋值传递, 是传递地址,而不是数字.
- 值传递机制: 针对基本数据类型
一.方法的形参的传递机制: 值传递
1.形参: 方法定义时,声明的小括号内的参数
2.实参,方法调用时,实际传递给形参的数据,可以是具体数也可以是变量
二.值传递机制: 如果参数是基本数据类型,此时实参赋给形参的是实参真实存储的数据值.
- 内存分析.
1.m和n没有换成的原因: 在栈中,先从main方法进来,main方法中定义的m, n分别赋值10, 20;
2.通过对象v调swap方法,v就不往里面写了,当调用swap方法时候,将上面实参m, n表示数据的值,赋给了下面swap方法的形参m, n(新定义的),这两个形参还要在栈中提供,形参m, n分别是实参赋值过来的,也是10和20,此时的m, n 是swap方法中的;
3.接着v调用swap方法就进入方法执行,一进去又声明新变量temp,temp拿了形参m赋的值,接着形参n的值给了m,接着又把temp给了n,最swap方法实现效果是把swap方法里的m和n交换,如果在方法里面输出,m和n就会交换,但是如果方法内不输出,swap方法执行完以后就销毁了(出栈), 销毁之后接着在main方法下面再输出,输出的m和n的值是main方法赋的值,因为swap方法出栈以后,输出的m和n保存的值是main里面定义的m和n两变量的值, main里面定义的变量在main的整个大括号区间里都是有效的,所以main里面输出的还是main里定义的值,所以两数没换成. 数组元素的值交换也是同理
public static void main(String[] args) { int m = 10; int n = 20; System.out.println(m + "," + n); ValueTransferTest1 test = new ValueTransferTest1(); test.swap(m,n); // 实参 /*int temp = m; m = n; n = temp;*/ System.out.println(m + "," + n); // 10, 20 } public void swap(int m,int n){ // 新变量实参传入的形参 int temp = m; m = n; n = temp; // System.out.println(m + "," + n); }
-
值传递机制: 针对引用数据类型
静态方法调用非静态方法要先new一个对象
静态方法调用静态方法直接调用
非静态方法调用非静态方法直接调用 -
内存分析
1.从main方法进来,在栈中new了一个data变量,也在堆空间中new了一个对象,该对象会有个地址值,并把地址值赋值给栈空间的data变量,通过地址值,栈空间中的data变量指向堆空间中的对象实体
2.接着下一步,data(对象)变量调用Data类中定义的变量m, n,一开始m, n的初始值都为0,通过对象调属性改了m, n的值,此时输出就是10, 20.
3.接着新建的对象去调swap方法,顺便把引用类型data传入作为形参,传入的引用类型data是上面的实参,所以形参是引用类型就存的是地址值,形参在栈中加载,相当于把main方法中data的地址值复制了一份,形参有了地址值后,他就指向堆空间中同一对象实体
4.进入swap方法体,通过data(形参)变量调用m的值,赋给一个swap方法内部声明的局部变量temp,temp也加载在栈中,data的n的值赋给data的m,temp的值又赋给了data的n,至此方法执行结束,结束后,swap方法定义的temp和data变量就出栈了,出栈后形参data的指针就没了,但是堆中对象还在,还有实参data的指向,这样判断堆中对象就不是垃圾,不能回收了.
5.回到main方法接着执行输出 data.m和data.n, 这两data变量是实参data, m和n的值就交换了
- 练习: 方法的参数传递
4.7.面向对象的特征一: 封装和隐藏
面向对象的特征一: 封装和隐藏
一.问题的引入:
当我们创建一个类的对象以后,我们可以童工"对象.属性"的方式,对对象的属性进行赋值,这里,赋值操作要瘦到属性的数据类型和存储范围的制约
除此之外,没有其他制约条件,但是在实际问题中,我们往往需要给属性赋值加入额外的限制调价.这个特条件就不能在属性声明时体现,我们只能通过方法进行限制条件的添加.(比如: setLegs())
同时,我们需要避免用户再使用"对象.属性"的方式对属性进行赋值.则需要将属性声明为私有的(private)
–>此时,针对于属性就体现了封装性
二.封装性的体现之一(不等同于封装性):
将类的属性xxx私有化(private),同时,提供公共的(public)方法来获取(getXxx)和设置(setXxx)此属性的值
拓展: 封装性的体现: 1.如上 2.不对外暴露的私有的方法 3.单例模式: 把构造器私有化
三.封装性的体现,需要权限修饰符来配合
1.Java规定的4种权限(从小到大排列): private,缺省,protected,public
2.4种权限可以用来修饰类及类的内部结构: 属性,方法,构造器,内部类,(代码块不行)
3.具体的,4中权限都可以用来修饰类的内部结构:属性,方法,构造器,内部类
修饰类的话,只能用: 缺省,public,不能用private
总结封装性: Java提供了4中权限修饰符来修饰类及类的内部结构,体现类及类的内部结构在被调用时的可见性的大小
体现一: 将类的属性xxx私有化(private),同时,提供公共的(public)方法来获取(getXxx)和设置(setXxx)此属性的值
体现二: 不对外暴露的私有的方法
体现三:单例模式(将构造器私有化,外边不能随便调构造器, 里面自己只造一个对象,大家都只拿一个对象用)
体现四: 如果不希望类在包外被调用,可以将类设置为缺省的.
public class AnimalTest { public static void main(String[] args) { Animal a = new Animal(); a.name = "大黄"; // a.age = 1; // 将属性私有化后不能直接调用,要通过public方法调用 // a.legs = 4; // 作用域不可见 The field Animal.legs is not visible a.show(); // 谁调的show方法,方法里显示的属性值就是谁的 a.setLegs(-6); // a.legs = -4; // The field Animal.legs is not visible a.show(); } } class Animal{ String name; private int age; // private好处:setLegs提供方法让legs赋值并加入判断条件,还能把直接调属性给禁掉 // 此时就能达到对 legs属性的封装,相当于这个属性没有对外暴露 private int legs; // 腿的数量 限制为私有权先,可见但类外面不能调用 public void setLegs (int l){ if (l >= 0 && l % 2 == 0){ legs = l; }else{ legs = 0; // 另一种写法,抛出一个异常(暂时没讲) } } public int getLges(){ return legs; } public void eat(){ System.out.println("动物进食"); } public void show (){ System.out.println("name = " + name + ",age = " + age + ",legs = " + legs); } // 提供关于属性age的get和set方法 public int getAge(){ return age; } public void setAge(int a){ age = a; } }
- 练习: 封装性的基本使用
public class PersonTest { public static void main(String[] args) { Person p1 = new Person(); // p1.age = 1; // age私有化后不能直接调用,编译不通过 p1.setAge(12); System.out.println("年龄为:" + p1.getAge()); } } public class Person { private int age; public void setAge(int a){ if (a < 0 || a > 130){ // throw new RuntimeException("传入数据非法"); // [[抛异常 System.out.println("传入数据非法"); return; // 结束方法 } age = a; } public int getAge(){ return age; } // 绝对不要把设置和获取的方法合起来写! 这些写方法没意义 /*public int doAge(int a){ age = a; return a; }*/ }
4.8.类的成员之三: 构造器(或构造方法)
- 类的结构之三,构造器(或构造函数(c++)或构造方法但又不同于方法,constructor)的使用
construct: 建设,建造. construction constructor: 建设者- 一.构造器的作用:
1.创建对象
2.初始化对象的属性- 二.说明:
1.如果没有显式的定义类的构造器的话,则系统默认提供默认无参构造器
2.定义构造器的格式(不同于方法): 权限修饰符 类名(形参列表){}
3.一个类中定义多个构造器,彼此构成重载
4.一旦我们显式的定义了类的构造器之后,系统就不在提供默认的无(空)参构造器(可用国家贫困补贴举例)
5.一个类中,至少会有一个构造器.(相当于给我们造对象的能力,有构造器才能造对象)(可能是默认的也可是自己写的)
public class PersonTest { public static void main(String[] args) { // 创建类的对象(构造器的使用): new + 构造器 (构造器和类同名) Person p = new Person(); // 自己定义了显式的构造器 p.eat(); Person p1 = new Person("tom"); System.out.println(p1.name); } } class Person{ int age; String name; // 构造器 public Person(){ // 可以用于初始化 比如实例化这个类时就需要传值,而不是在调用方法才传值 // 构造器里的代码在new对象的时候就执行了 System.out.println("Person()....."); } // 构造器的重载 public Person(String n){ // 形参的作用 初始化造的对象当前的属性 name = n; // name: 当前正在创建的对象的属性 } public Person(String n, int a){ name = n; age = a; } public void eat(){ System.out.println("人吃饭"); } public void study(){ System.out.println("人睡觉"); } }
- 练习: 构造器的基本使用
1.创建程序,在其中定义两个类:Person和PersonTest类.定义如下:
用setAge()设置热的合法年龄(0~130),用getAge()返回人的年龄
2.1.在前面定义的Person类中添加构造器,利用构造器设置所有人的age属性初始值都为18
2.2.修改上题中类和构造器,增加name属性,使得每次创建Person对象的同时初始化对象的age属性值和name属性值
3.如果类中提供了至少一个构造器,但是没有提供空参构造器,那么构造对象时如果不提供参数就是不合法的.也就是创建没有形参的对象会报错
/* * 在PersonTest类中实例化Person类的对象b * 调用setAge()和getAge()方法,体会Java的封装性 */ public class PersonTest { public static void main(String[] args) { Person p1 = new Person(); // p1.age = 1; // age私有化后不能直接调用,编译不通过 p1.setAge(12); // p1.getAge(); // age == 18 System.out.println("年龄为:" + p1.getAge()); Person p2 = new Person("tom", 21); // 属性私有化后只能通过方法来调用 System.out.println("name: " + p2.getName() + ", age: " + p2.getAge()); } } public class Person { // 代码顺序: 属性-->构造器-->方法 private int age; private String name; public Person(){ age = 18; } // 体现了有参构造器 public Person(String n, int a){ name = n; age = a; } public void setAge(int a){ if (a < 0 || a > 130){ // throw new RuntimeException("传入数据非法"); // 抛异常 System.out.println("传入数据非法"); return; // 结束方法 } age = a; } public int getAge(){ return age; } // 体现了封装性 public void setName(String n){ name = n; } public String getName(){ return name; } // (错误写法)绝对不要把设置和获取的方法合起来写! 这些写方法没意义 /*public int doAge(int a){ age = a; return a; }*/ }
- 构造器练习: 三角形
/* * 编写两个类,TriAngle和TriAngleTest,其中TriAngle类中声明私有的底边长base和高height,同时声明公共方法访问私有变量. * 此处,提供类必要的构造器.另一个类中使用这些公共方法,计算三角形的面积 */ public class TriAngleTest { public static void main(String[] args) { TriAngle t1 = new TriAngle(); t1.setBase(3.2); // 相当于传入实参 t1.setHeight(4.66); System.out.println("base: " + t1.getBase() + ", height: " + t1.getHeight()); // 构造器一方面可创建对象,同时可给相应对象的属性赋值 TriAngle t2 = new TriAngle(5.1, 4.6); System.out.println("base: " + t2.getBase() + ", height: " + t2.getHeight()); } } public class TriAngle { private double base; private double height; public TriAngle(){ } public TriAngle(double b,double h){ base = b; height = h; } public void setBase(double b){ base = b; } public double getBase(){ return base; } public void setHeight(double h){ height = h; } public double getHeight(){ return height; } }
- 总结: 属性赋值的先后顺序
1.默认初始化(类属性默认值)
2.显式初始化(给类中属性赋值)
3.构造器中初始化(赋值)
=上面三个操作只能执行一次,下面的可以反复执行===
4.未封装通过"对象.属性"的方式,赋值或封装后通过"对象.方法名()",赋值
以上操作的先后顺序: 1 - 2 - 3 - 4,从后往前覆盖,前面的都作为过程出现
public class UserTest { public static void main(String[] args) { User u = new User(); System.out.println(u.age); User u1 = new User(2); u1.setAge(3); System.out.println(u1.age); } } class User{ String name; int age = 1; public User(){ } public User(int a){ age = a; } public void setAge(int a){ age = a; } }
4.9.关键字: this的使用—this调用属性和方法
this关键字的使用:
- 1.this可用来修饰: 属性,方法,构造器
- 2.this修饰属性和方法:
this理解为: 当前对象或当前正在创建的对象
- 2.1.在类的方法中,我们课使用"this.属性"或"this.方法"的方式,调用当前对象属性或方法.
但是,通常情况下,我们都选择省略"this. ".特殊情况下,如果方法的形参和类的属性同名时,必须显式的使用"this.变量"的方式,表明此变量是属性,而非形参.- 2.2.在类的构造器中,我们可使用"this.属性"或"this.方法"的方式,调用当前正在创建的对象属性或方法,但是,通常情况下,都选择省略"this.",特殊情况下,如果构造器的形参和类的属性同名时,必须显式的 使用"this.变量"的方式,表明此变量是属性,而非形参.
public class PersonTest { public static void main(String[] args){ Person p1 = new Person(); // 使用默认的空参构造器 p1.setAge(1); System.out.println(p1.getAge()); p1.eat(); } } class Person{ private String name; // 类的成员变量(属性) private int age; public Person(){ this.eat(); // 调当前正在创建的对象的eat方法 } // 构造器中的this: 当前正在创建的对象 public Person(String name){ this.name = name; // 当前正在创建的对象的属性 } public Person(int age){ this.age = age; } public Person(String name, int age){ this.name = name; this.age = age; } public void setName(String name){ // 属性和形参(局部变量)同名不报错因为地位不同,一个在类中一个在方法中 this.name = name; // 就近原则,优先考虑近的变量名 } public String getName(){ return this.name; } public void setAge(int age){ // age = 1 // age = age; // 1 = 1,跟属性age无关了 // this: 可理解为当前对象,相当于p1 this.age = age; // 当前对象的属性/方法 通过对象调方法或属性都用点 } public int getAge(){ return this.age; } public void eat(){ System.out.println("人吃饭"); this.study(); } public void study(){ System.out.println("人学习"); } }
-
this调用构造器
this关键字的使用:
1.this可用来修饰: 属性,方法,构造器
2.this修饰属性和方法:
this理解为: 当前对象或当前正在创建的对象
2.1.在类的方法中,我们课使用"this.属性"或"this.方法"的方式,调用当前对象属性或方法.
但是,通常情况下,我们都选择省略"this. “.特殊情况下,如果方法的形参和类的属性同名时,
必须显式的使用"this.变量"的方式,表明此变量是属性,而非形参.
2.2.在类的构造器中,我们可 使用"this.属性"或"this.方法"的方式,调用当前正在创建的对象属性或方法,
但是,通常情况下,都选择省略"this.”,特殊情况下,如果构造器的形参和类的属性同名时,必须显式的
使用"this.变量"的方式,表明此变量是属性,而非形参.
3.this调用构造器
(1)在类的构造器中,可显式的使用"this(形参列表)"方式,调用本类中指定的其他构造器
(2)构造器中不能通过"this(形参列表)“方式调用自己
(3)如果一个类中由n个构造器,则最多有n-1个构造器中使用了"this(形参列表)”
(4)规定: "this(形参列表)“必须声明当前构造器的首行
(5)构造器内部,最多只能声明一个"this(形参列表)”,用来调用其他的构造器
public class PersonTest { public static void main(String[] args){ Person p1 = new Person(); // 使用默认的空参构造器 p1.setAge(1); System.out.println(p1.getAge()); p1.eat(); System.out.println(); // 构造器多重调用也只是创建了一个对象,只是调用其他构造器时逻辑借用了其他构造器的逻辑 // 不管通过什么方式调用构造器,最终也只是造了一个对象 Person p2 = new Person("Harry", 20); System.out.println(p2.getAge()); System.out.println(p2.getName()); } } class Person{ private String name; // 类的成员变量(属性) private int age; public Person(){ // this.eat(); // 调当前正在创建的对象的eat方法 // this(); // 空参构造器不能调自己,会死循环,两个构造器也不能来回调 String info = "Person初始化时,需要考虑如下的1,2,3,4...(共40行代码)"; System.out.println(info); } // 构造器中的this: 当前正在创建的对象 public Person(String name){ this(); // 调用构造器的方法,参数是什么,就是调哪个构造器 this.name = name; // 当前正在创建的对象的属性 } public Person(int age){ this(); this.age = age; } public Person(String name, int age){ this(age); this.name = name; // 再初始化独立的name // this.age = age; 调用上面的构造器后可省略 } public void setName(String name){ // 属性和形参(局部变量)同名不报错因为地位不同,一个在类中一个在方法中 this.name = name; // 就近原则,优先考虑近的变量名 } public String getName(){ return this.name; } public void setAge(int age){ // age = 1 // age = age; // 1 = 1,跟属性age无关了 // this: 可理解为当前对象,相当于p1 this.age = age; // 当前对象的属性/方法 通过对象调方法或属性都用点 } public int getAge(){ return this.age; } public void eat(){ System.out.println("人吃饭"); this.study(); } public void study(){ System.out.println("人学习"); } }
- 综合练习
public class CustomerTest { public static void main(String[] args) { Customer cust = new Customer("Jane", "Smith"); Account acct = new Account(1000, 2000, 0.0123); // 声明构造器 cust.setAccount(acct); // 银行办卡后得到了卡号 cust.getAccount().deposit(100); // 类变量的运用 cust.getAccount().withdraw(960); cust.getAccount().withdraw(2000); System.out.println("Customer [" + cust.getLastName() + "," + cust.getFirstName() + "] has a account: id is " + cust.getAccount().getId() + ", annualInterrestRate is" + cust.getAccount().getAnnualInterestRate() * 100 + "%, balance is " + cust.getAccount().getBalance()); } } public class Account { private int id; // 账号 private double balance; // 余额 private double annualInterestRate; // 年利率 // 构造器 public Account(int id,double balance,double annualInterestRate){ this.id = id; this.balance = balance; this.annualInterestRate = annualInterestRate; } public void setId(int id){ this.id = id; } public int getId(){ return this.id; } public void setBalance(double balance){ this.balance = balance; } public double getBalance(){ return this.balance; } public void setAnnualInterestRate(double annualInterestRate){ this.annualInterestRate = annualInterestRate; } public double getAnnualInterestRate(){ return this.annualInterestRate; } public void withdraw(double amount){ // 取钱 if (balance < amount){ System.out.println("余额不足,取钱失败"); return; // 和else作用相同 } balance -= amount; System.out.println("成功取出" + amount); } public void deposit(double amount){ // 存钱 if (amount > 0){ balance += amount; System.out.println("成功存入" + amount); } } } public class Customer { private String firstName; private String lastName; private Account account; // 构造器 public Customer(String f,String l){ this.firstName = f; this.lastName = l; } public String getFirstName(){ return this.firstName; } public String getLastName(){ return this.lastName; } public void setAccount(Account account){ this.account = account; } public Account getAccount(){ return this.account; } }
- 综合练习2: 对象数组
public class BankTest { public static void main(String[] args) { Bank bank = new Bank(); bank.addCustomer("Jane","Smith"); // 连续操作 可能会空指针,方法要有返回值才能连续操作 bank.getCustomer(0).setAccount(new Account(2000)); bank.getCustomer(0).getAccount().withdraw(500); double balance = bank.getCustomer(0).getAccount().getBalance(); System.out.println("客户:" + bank.getCustomer(0).getLastName() + "的账户余额为: " + balance); bank.addCustomer("万里","杨"); System.out.println("银行客户个数: " + bank.getNumberOfCustomers()); } } public class Customer { private String firstName; private String lastName; private Account account; // 构造器 public Customer(String f,String l){ this.firstName = f; this.lastName = l; } // get,set方法 public String getFirstName(){ return this.firstName; } public String getLastName(){ return this.lastName; } public void setAccount(Account acct){ this.account = acct; } public Account getAccount(){ return this.account; } } public class Account { private double balance; public Account(double init_balance){ this.balance = init_balance; } // 余额查询 public double getBalance(){ return this.balance; } // 存钱操作 public void deposit(double amt){ if (amt > 0){ balance = balance + amt; System.out.println("存款成功"); } } // 取钱操作 public void withdraw(double amt){ if (balance > amt){ balance = balance - amt; System.out.println("取钱成功"); }else{ System.out.println("余额不足"); } } } public class Bank { // 定义了变量,未初始化,但是属性是已经默认初始化了的,此时默认值为null private Customer[] customers; // 存放多个用户的数组 private int numberOfCustomers; // 记录客户的个数 // 构造器 public Bank(){ customers = new Customer[10]; // 先要给数组初始化空间 因为权限是private所以不能再测试类main里初始化 } // 添加客户方法 public void addCustomer(String f,String l){ // cust是customer类型变量,上面定义的数组也是customer类型的,所以可以赋值 Customer cust = new Customer(f, l); // customers[numberOfCustomers] = cust; // 可以理解为 customers[numberOfCustomers] = new Customer(f,l); 数组元素在构造器中静态赋值 // numberOfCustomers++; // customers = new Customer[10]; // 数组空间初始化不能写在这,因为,本来是是造构造器中造好一个数组后掉一个方法存一个人再调方法再存一个人,写在方法里变成,先调方法造好一个数组后再存一个人,再调一次方法又重新造一个数组再重新存一个人 customers[numberOfCustomers++] = cust; } // 获取客户个数的方法 public int getNumberOfCustomers(){ return numberOfCustomers; } // 获取指定位置上的客户的方法 public Customer getCustomer(int index){ // return customers[index]; // 不能这样写,可能报异常1.数组未初始化时可能空指针,2.数组初始化后可能会数组下标越界,3.index也许不会超出数组长度,但是大于实际存入的数组元素个数也不行. if (index >= 0 && index < numberOfCustomers){ // 索引值小于客户数,因为第一个客户放在了第0位 return customers[index]; } return null; } }
4.10.1.package关键字的使用
- 1.为了更好地实现项目中类的管理,提供包的概念
- 2.使用package声明类或接口所属的包,声明在源文件的首行
- 3.包,属于标识符,遵循标识符的命名规则,规范(xxx.yyy.zzz),“见名知意”
- 4.每"."一次,就代表一层文件目录.
补充:同一个包下不能命名同名的接口,类
不同的包下,可以命名同名的接口,类
4.10.2.import关键字的使用
5.1.面向对象特征之二: 继承性的理解
1.为什么要有类的继承性?(继承性的好处)
1.减少了代码的冗余,提高了代码的复用性
2.便于功能的扩展: 把子类都想扩展的功能直接声明在父类当中,子类又因为继承了,直接就可以拿到
3.为之后多态性的使用,提供了前提
2.继承性的格式:
class A extends B{}
A: 子类,派生类,subclass
B: 父类,超类,基类,superclass
3.子类继承父类以后有哪些不同?
2.1.体现: 一旦子类A继承父类B以后,子类A中就获取了父类B中声明的所有:属性,方法 (用来调用)
特别地,父类中声明为private的属性或方法,子类继承父类以后,仍然认为获取了父类中私有的结构
只有因为封装性的影响,使得子类不能直接调用父类的结构而已
2.2.子类继承父类以后,还可以声明自己特有的属性或方法,实现自己特有的功能的拓展
子类和父类的关系,不同于子集和集合的关系(父类有的子类一定有,子类有的父类不一定有)
4.Java中继承的说明
1.一个父类可以被多个子类继承.
2.Java中**类的单继承性:**一个类只能有一个父类;(接口可以多继承)
3.子父类是相对的概念
4.子类直接继承的父类,称为: 直接父类;简介继承的父类称为:间接父类
5.子类继承父类后,就获取了直接父类以及所有间接父类中声明的属性和方法
5.java.lang.Object类的理解
特别声明是java.lang包下的Object类因为可能自己会创建同名的Object类
1.如果我们没有先是的声明一个类中的父类的话,则此类继承于java.lang.Object类
2.所有的java类(除java.lang.Object类之外)都直接或间接的继承于java.lang.Object类
3.所有java类具有java.lang.Object类声明的功能
这篇关于java基础的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-28MQ底层原理资料详解:新手入门教程
- 2024-11-28MQ项目开发资料详解:新手入门教程
- 2024-11-28MQ项目开发资料详解:入门与初级用户指南
- 2024-11-28MQ消息队列资料入门教程
- 2024-11-28MQ消息队列资料:新手入门详解
- 2024-11-28MQ消息中间件资料详解与应用教程
- 2024-11-28MQ消息中间件资料入门教程
- 2024-11-28MQ源码资料详解与入门教程
- 2024-11-28MQ源码资料入门教程
- 2024-11-28RocketMQ底层原理资料详解