1.如何做到不通过临时变量交换两个变量的值
// 不同过临时变量交换变量值
int a = 10;
int b = 20;
a = a^b;
b = a^b;
a = a^b;
异或运算两次不改变数值
2.两种数组翻转的应用
public class Conclusion {
public static void main(String[] args) {
int [] arr = {1,2,3,4,5,6};
for (int star = 0,end = arr.length-1; star < end; star ++,end --) {
int temp = arr[star];
arr[star] = arr[end];
arr[end] = temp;
}
for (int i = 0; i < arr.length/2; i++) {
int temp = arr[i];
arr[i] = arr[arr.length-1-i];
arr[arr.length-1-i] = temp;
}
}
}
3.二维数组
int [][] arr = new int[2][3];
// 打印此代码得到二维数组的地址 [[I@0x0011
System.out.println(arr);
// 打印此代码得到一位数组的存储地址 [I@0x0012
System.out.println(arr[0]);
// 打印此代码得到二维数组中的一位数组中存储的内容 20
System.out.println(arr[0][0]);
4.成员变量和局部变量的区别
变量名 | 成员变量 | 局部变量 |
---|---|---|
存储 | 堆内存 | 栈内存 |
所处位置 | 类中方法外 | 方法中 |
生命周期 | 随对象的存在而存在 | 随方法的调用存在而存在 |
初始值 | 有对应的初始值 | 没有初始值,定义需赋值 |
特殊的:for 循环中初始化语句的变量虽然属于局部变量,但是他的生命周期随着 for 循环的结束而结束。
5.有参数的构造方法和无参数的构造方法的区别
(1).构造过程的代码区别
public class Phone {
private int price;
private String brand;
//无参数的构造方法
public Phone() {}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
//有参数的构造方法
public Phone(String brand, int price) {
this.brand = brand;
this.price = price;
}
}
(2).运行过程中的存储区别
无参数:main 方法在栈内存中调用 setPrice和 setBrand成员方法来初始化对象
有参数:不会调用成员方法,不需入栈内存。
(3).在后期数据处理上
public class PhoneTest {
public static void main(String[] args) {
//对象 1
Phone i = new Phone();
i.setBrand("iPhone");
i.setPrice(5999);
System.out.println(i.getBrand() + ":" + i.getPrice()); //iPhone:5999
i.setPrice(6999);
System.out.println(i.getBrand() + ":" + i.getPrice());//iPhone:6999
//对象 2
Phone m = new Phone("xiaomi",4999);
System.out.println(m.getBrand() + ":" + m.getPrice());//xiaomi:4999
Phone newM = new Phone("xiaomi",5999);
System.out.println(m.getBrand() + ":" + m.getPrice());//xiaomi:4999
}
}
无参数:更改对象内容是调用成员方法,通过i.setPrice() 来更改之前录入的数据。
有参数:通过创建新对象更改姓名(若不使用 m.setPrice()),会导致之前的对象在堆内存中成为垃圾。
综上:虽然在创建类时使用有参数的构造方法更便捷,但是为了程序的健壮性,还是需要设置 set和get成员方法。
常用 API 的方法、区别与特点
1.Scanner 中next() 和nextLine() 的区别
next():检测到第一个有效值开始(之前的空格自动省略),有效值之后检测到空格结束。结束标志:空格、回车、tab
nextLine():可以完整的接受全部信息。结束信息:回车
例如:next():输入 123 321 只能接受到 123
nextLine():输入 123 321 可以接受到 123 321(包括空格)
//使用方法
String s1 = sc.nextLine();
String s2 = sc.next();
特殊的:
当 nextLine()和 nextInt()一起使用时。
int i = sc.nextInt;
//输入10 + 回车换行
String s = sc.nextLine;
//检测到回车换行会认为是结束信息,从而结束输入。
2.String 中方法的特点、重点
方法 | 说明 |
---|---|
public boolean endsWith(String s ) | 判断字符串是否以参数s为结尾 |
public boolean starsWith(String s ) | 判断字符串是否以参数s开头 |
(1)java 中 String 在 java.lang 包中,属于核心包,不需要导包
(2)字符串在创建以后不可改变
(3)打印对象是不会输出地址,与其他类型不同!
String s = "abc";
System.out.println(s);
//结果是 abc 而非 s 的地址,因为在使用打印时,系统会默认加上 toString(s)
(4)几种定义 String 对象的方法
//第一种,String 引用类型直接定义(最常用)
String s1 = "abc";
//第二种,空参数构造
String s2 = new String();
//第三种,带参数构造,通过字符串(有点多余)
String s3 = new String("abc");
//第四种,带参数构造,通过字符数组
char[] chs = {'a','b','c'};
String s4 = new String(chs);
(5)关于几种定义方法的区别
第一种:直接定义,在运行时系统会在字符串池中查找,有相同的时候直接引用,没有相同的时候创建。
第三种:abc 在字符串池和堆内存中同时存在,堆内存中以对象方式存在。
(6)equal()和 = = 以及 String 在创建时的地址
= = 在对比基本数据类型时,比较的是数值是否相同
== 在对比引用数据类型时,比较的是地址值是否相同
equal()在使用时可以对比两个引用数据类型的内容是否相同(区分大小写)
equalsIgnoreCase()在使用时不区分大小写。
使用方法
int a =10,b = 20;
boolean tf1 = a == b; //false
String s1 = "abc";
String s2 = "abc";
String s3 = new String("abc");
char[] chs = {'a','b','c'};
String s4 = new String(chs);
String s5 = "a" + "b" + "c";
String s6 = "ab";
String s7 = s6 + "C";
StringBuilder s8 = "abc"
boolean tf2 = s1 == s2; //true
boolean tf3 = s2 == s3; //false
boolean tf4 = s1 == s4; //false
boolean tf5 = s3 == s4; //false
boolean tf6 = s1.equals(s2);//true
boolean tf7 = s1 == s5; //true
boolean tf8 = s1 == s7; //false
boolean tf9 = s1 == s8; //false
tf1:数值之间的= =,数值相同为 true
tf2:字符串之间的 = =,这里的 true 不是因为同为 abc,而是因为s1 在创建是添加了 abc 到字符串池中,s2 创建时首先检查字符串池中有没有相同的字符串,有则复制,没有则新建。这里 s2 创建时字符串池中有 abc 则直接引用字符串池中的 abc 地址则自然与s1 相同,所以s1 == s2
tf3:s2记录的是字符池中abc 的地址,s3 虽然会到字符串池中找 abc 但 new 就一定记录的是堆内存中对象的地址。所以不同。
tf4:原理与 tf3 相同
tf5:属于不同的对象,记录的地址值自然不同。
tf6:这里 s1与s2内容相同,s1 到 s5 内容都相同。
tf7:相等的原理是Java 虚拟机存在优化机制。
tf8:不相等是因为s6 是变量,变量+常量默认使用 StringBuilder将变量与字符串拼接,然后转成 String 形式,转换时创建新的String对象。
tf9:根据底层原理,String 调用 equals 时会先判断参数是否为 String ,不是则返回 false
(7)char 与 String 的配合
//String中 public char charAt(),字符串中 0 位置的字符
s.charAt(0);
//String 中 public int length(),字符串的长度,注意调用时有()与数组.length 不同
int a = s.length();
// String中 public char[] toCharArray(),将字符串拆分成字符数组
char[] chars = s.toCharArray();
//String的 subString(int n ,int m)方法,n 为起始位置 m 为终止位置[n,m) 包括 n 不包括 m 。(0,3)表示前三个(012) ,subString(int n)表示从n索引开始.
String s1 = s.subString(0,3);
//String的 replace(CharSequence target, CharSequence replacement)方法,(需要替换的目标,替换后的内容)
String s2 = s.replace("TMD","***");
//String的split(String regex) 方法,如果字符串组有规律的组和,可以通过规律分割字符串组,例如 "张三,23"
String[] sArr = s.split(",");
//返回索引值
String s = "123/456"
int i = s.lastIndexOf("/");// i=3
3. StringBuilder
(1)与 String的区别:
String:长度不可变
StringBuilder:长度可变
(2)两种构造方法
StringBuilder sb1 = new StringBuilder();
StringBuilder sb2 = new StringBuilder(s1);
特殊的:第二种构造方法可以视为将 String 转化为 StringBuilder
(3)
StringBuilder sb3 = sb1.append();//StringBuilder拼接括号内的任意值,返还对象本身
sb1.append().append();//链式编程
StringBuilder sb4 = sb3.reverse;//字符串逆置,返还对象本身
String s = sb1.toString();//StringBuilder 转化为 String
4.String,char,int 相互转换
1.s.toCharArray
2.s.charAt(i)
//String -> char
String a = "abc";
//方法一:将 String 变成 char数组
char[] chars = a.toCharArray();
//方法二:得到数组中索引位置的字符
a.charAt(0);
1.Interger.parseInt(String s);
2.Interger.valueOf(String s).intValue();
//String -> int
String s = "123";
//方法一:
int a = Integer.parseInt(s);
//方法二:
int b = Integer.valueOf(s).intValue();
1.String.valueOf(chars[i]) ;
2.String.valueOf(chars);
//char -> String
char[] chars = {'a', 'b', 'c' };
//方法一:配合 for 循环
String s;
for (int i = 0; i < chars.length; i++) {
s = String.valueOf(chars[i]);
System.out.println(s);//输出了三个字符串类型分别是"a","b","c"
}
//方法二将字符数组转换成一个字符串
String s2 = String.valueOf(chars);
System.out.println(s2);
1.利用Ascii 码
2.char -> String -> int
3.char -> Character -> String -> int 太蠢了
//char -> int
char ch = '8';
//方法一:
int a = ch - 48;
//方法二:
String s = String.valueOf(ch);
int b = Integer.parseInt(s);
//方法三:
Character character = ch;
int c = Integer.parseInt(character.toString());
1.利用 String 的优化机制
2.String.valueOf(s)
3.Inter.toString(s)
//int -> String
int a = 789;
//方法一:
String s2 = "" + a;
//方法二:
String s = String.valueOf(a);
//方法三:
String s1 = Integer.toString(a);
也是利用 ASCII 码,但 int -> char 必须强制转换
//int -> char
int a = 8;
char c = (char) (a + 48);
System.out.println(c);
5. ArrayList
//构造,<数据类型>,尖括号的内容可以限制集合的内容
ArrayList<String> arr = new ArrayList<>();
//增,往数组中添加内容。可用 Boolean 接收,判断是否接收成功
boolean t = arr.add("abc")
arr.add(0,"abc");//指定位置添加
//删
arr.remove("abc");//删除第一个"abc"
arr.remove(0); //删除第 0 个
//改
arr.set(0,"abc");//0位置改成 "abc"
//查
arr.get(0);//查找 0 位置
分类思想、Static
分类思想 :
将任务分工给不同的类来完成。
Controller 类:用来完成与用户交互的一系列动作。比如:接收用户的需求,用户想要的是添加学生还是删除学生。采集用户的信息,用户输入的姓名年龄等。在控制台输出提示语句。
Service 类:用来完成逻辑方面的处理。比如当用户输入学号时,判断这个数组是不是空的,学号是不是重复的等等这些类似的逻辑
Dao 类:用于数据存储方面,数据的增删改查。比如将学生类加入到数组中。
添加学生这一功能的实现逻辑: 前台收集用户输入的信息,业务员帮助前台判断信息是否正确,前台将正确的信息封装成对象交给业务员,业务员交给仓库管理员,仓库管理员完成信息的添加,告诉业务员完成了,业务员告诉客服完成了,客服再告诉用户完成了。
通过这种通俗的讲解,还可以体会到为什么要分类,分类之后谁哪个环节出现问题,可以很快的定位到是那一层的问题,方便后期维护。
Static
Static 是个修饰符,可以修饰成员变量和成员方法。
特点:1.可以被类中的所有对象共享。比如,修饰成员变量 ClassName 时,所有的学生类共享这个属性,大家都是相同的 ClassName,一个对象改变了 ClassName 所有对象的 ClassName 都会更改.
2.随着类的加载而加载,优先于对象存在。比如Static修饰成员方法时,即使不创建对象也可以调用类的方法,因为静态修饰后这个方法随着类的字节码文件加载而加载的,调用时存储在堆内存的静态存储位置。
3.可以用类名调用。这里需要注意的是 Static 修饰后的成员既可以用类名调用,也可以用对象名调用,而没有被修饰的只能用对象调用。
注意事项
1.静态方法只能访问静态成员。静态修饰后会随着类的加载而加载,但是没被修饰的还没有被加载,所以不能调用。
2.非静态方法可以访问静态成员,静态的先加载好了,非静态的后加载,当然可以使用已经加载好的成员。
3.静态方法中没有 this 关键字,因为 this 代表的是对象,而静态成员是在对象被创建之前就加在好了,所以静态方法不能使用还没有加载好的 this 关键字。
继承
(1)继承的实现:通过 extends 关键字,子类 extends 父类{}
(2)注意事项:Java 中只有单继承和多层继承
(3)继承中访问变量的方式:就近原则
1.子类局部 2.子类成员范围 3.父类成员范围。若没有找到就报错(不查找多层继承!)
(4)super: 代表父类存储空间的标识(可以理解成调用父类的对象)
this:代表本类对象的引用
super和 this 的使用方法
super:super.成员方法和成员变量 super(参数)访问父类的有参构造方法
this: this.成员方法和成员变量 this(参数)访问本类中的有参构造方法
抽象类的模板设计
//模板类
public abstract class Witter {
public void write(){
System.out.println("《 content 》");
body();
System.out.println("ending");
}
public abstract void body();
}
//调用模板的对象类
public class Tom extends Witter{
@Override
public void body() {
System.out.println("content");
}
}
//测试类
public class Test {
public static void main(String[] args) {
Tom t = new Tom();
t.write();
}
}
接口
接口类的定义:public Interface 接口名 {}
实现类:class 实现类名 implements 接口名 {}
接口与类的关系
类之间:只能是继承关系,单继承和多层继承。
类与接口:实现关系,可以单实现,多实现。
接口之间:继承关系,可以单继承,也可以多继承。
接口特点
接口中的定义
(1)变量(常量):省略了 public static final (意味着接口中的常量定义后不可改变,)
(2)方法:省略了 public abstract
(3)默认方法:(public) default 返回值 方法名() {} (与 main 函数中的方法类似)
注意事项: 默认方法可以被调用,不要求重写,重写时不写 default 关键字(主要用于接口升级时)
(4)静态方法:(public) static 返回值 方法名() {}
注意事项: 静态方法只能使用类名调用。***(接口类的静态方法不能被实现类使用,但父类的静态方法可以被子类使用。)***
(5)私有方法: private (static) 返回值 方法名 (){} 在接口类中提取出的方法,类似于 main 函数中提取方法
注意事项: 默认方法可以调用静态和非静态的方法,静态方法只能调用静态私有方法。
接口的使用思路
一个类中都是抽象方法时可以将类转化为接口
涉及到接口升级或接口加入大量新抽象方法时可以使用默认方法。
如果需要更简洁的调用默认方法,可以将默认方法变为静态方法(default 换成 static)
如果接口内想提取方法,可以用private 修饰成私有方法(需要去掉 default 关键字)
多态
多态的实现
public abstract class Animal {
public abstract void eat();
}
class Cat extends Animal{
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}
class Dog extends Animal{
@Override
public void eat() {
System.out.println("狗吃肉");
}
}
//测试类
public class Test03 {
public static void main(String[] args) {
eat(new Dog());
eat(new Cat());
}
public static void eat(Animal a ){
a.eat();
}
}
注意事项:
(1)要有继承或实现。Cat extends Animals 或 InterImpl implement Interface
(2)要有方法的重写。因为编译成员方法时看父类是否存在方法,执行时看子类是否存在方法
(3)要有父类引用指向子类对象。
上述代码中继承和重写显而易见。父类引用指向子类对象的基本表现是
Animals a = new Dog();
上述代码提取出了一个静态方法 方法调用的参数是 Animal a 所以当调用方法时
eat(new Dog());
等价于
Animal a = new Dog();
eat(a);
多态访问成员
访问成员变量:编译时看父类,运行时看父类。
访问成员方法时:编译时看父类,运行时看子类。
原因:new 子类对象时会先创建父类对象,堆内存中的子类对象有个super指向父类的地址区域。编译访问成员变量时,会查看父类是否有该变量,没有则报错,运行时通过子类的 super 访问父类的成员变量。
而编译访问方法时,首先看父类是否存在该方法,不存在则报错。运行时因为有子类的方法重写,所以会运行子类的方法。
多态的特点
优点:可以在写方法时传父类参数,调用方法时传子类参数。不同的子类可以调用相同方法。
缺点:调用方法时不能调用子类独有的方法。
多态的转型与判断
向上转型: Animal a = new Dog(); 与多态实现的前提第三点相同。将实际类型是 Dog 的 a 转型为 Animal
向下转型:Dog d = (Dog) a;
转型的判断: a instanceof Dog
判断a 的实际类型是否是 Dog,是返回 true。常和 if联用
内部类
成员内部类
类中与方法并肩
创建对象格式:Outer.Inner 对象名 = new Outer().new Inner();
访问特点:内部类可以使用外部类的成员,包括私有。但外部类访问内部类成员时只能创建对象。
例如:内部类访问外部类的成员变量时 OuterClass.this.变量名
(1)私有成员内部类
private 修饰内部类,测试类调用时,只能在外部类中创建内部类调用的放法,方法中创建内部类的对象。
(2)静态成员内部类
static 修饰内部类
创建对象格式:Outer.Inner 对象名 = new Outer.Inner();
局部内部类
与私有内部类类似。在方法中定义内部类后,在创建对象以便于测试类调用。
匿名内部类
格式: new 类名/接口名() {重写方法};
常用于 方法参数是对象时
例如:show(new 类名() {@overridr});
其中 new 类名() {@overridr} 指的是一个对象,对象能做的它都能做
Lambda
前提:只能用于接口,且接口内只能有一个抽象方法。
格式: () ->{} 小括号内是参数,大括号内是重写后的方法
简写方法:小括号内只有一个参数时可以去掉小括号,参数类型可去掉,但必须所有都去掉。
大括号内只有一行代码时可以去掉大括号、分号和 return
应用场景:常用于简写匿名内部类