数组循环题
JDK,JRE,JVM三者关系,JDK,JRE的结构有哪些
JDK是Java开发工具包,JRE是Java运行环境,JVM是Java虚拟机
JDK = JRE + Java开发工具
JRE = JVM + Java核心类库
一维数组初始化的两种方式
int[] i = new int[]{…};
int[] i = new int[10];
二维数组初始化的三种方式
int[][] i = new int[10][10];
int[][] i = new int[10][];
int[][] i = new int[][]{{},{}…}
如何遍历二维数组
for(int i = 0;i < arr.length;i++){
for(int j = 0;j < arr[i].length;j++){
System.out.println(arr[i][j]);
}
}
----------------------------------------------
求100以内的质数
能整除1和本身的数就是质数
public class Test {
public static void main(String[] args) {
//求100以内的质数:能整除1和本身的数就是质数
for (int i = 2; i < 101; i++) {
//标记,重置
boolean flag = true;
for (int j = 2; j < i; j++) {
if (i % j == 0){
flag = false;
break;
}
}
//循环完成判断flag,如果是false表示不是质数,是true就是质数
if (flag){
System.out.println(i + "是质数!");
}
}
}
}
结果:2是质数!3是质数!5是质数!7是质数!11是质数!13是质数!17是质数!19是质数!23是质数!29是质数!31是质数!37是质数!41是质数!43是质数!47是质数!53是质数!59是质数!61是质数!67是质数!71是质数!73是质数!79是质数!83是质数!89是质数!97是质数!
-----------------------------------------------
求1000以内的完数:一个数等于它的因子之和的数就是完数,比如:完数6,它有约数1、2、3、6,除去它本身6外,其余3个数相加,1+2+3=6
public class Test {
public static void main(String[] args) {
/*求1000以内的完数:一个数等于它的因子之和的数就是完数,
比如:完数6,它有约数1、2、3、6,除去它本身6外,其余3个数相加,1+2+3=6*/
for (int i = 6; i < 1001; i++) {
//记录总数
int count = 0;
for (int j = 1; j < i; j++) {
if (i % j == 0){
count += j;
}
}
if (i == count){
System.out.print(i + "是完数!");
}
}
}
}
结果:6是完数!28是完数!496是完数!
------------------------------------------------
使用二维数组打印10行杨辉三角
提示:
第一行有1个元素,第N行有N个元素
每行第一个和最后个元素是1
第三行开始,非第一个元素和最后一个元素,即yanghui[i][j] = yanghui[i-1][j-1] + yanghui[i-1][j]
public class Test {
public static void main(String[] args) {
//声明二维数组
int[][] yangHui = new int[10][];
//循环赋值
for (int i = 0; i < yangHui.length; i++) {
yangHui[i] = new int[i + 1];
//元素首末赋值1
yangHui[i][0] = 1;
yangHui[i][i] = 1;
//再次循环赋值
if (i > 1) {
for (int j = 1; j < yangHui[i].length - 1; j++) {
yangHui[i][j] = yangHui[i - 1][j - 1] + yangHui[i - 1][j];
}
}
}
//遍历二维数组
for (int i = 0; i < yangHui.length; i++) {
for (int j = 0; j < yangHui[i].length; j++) {
System.out.print(yangHui[i][j] + " ");
}
System.out.println();
}
}
}
结果:
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1
1 7 21 35 35 21 7 1
1 8 28 56 70 56 28 8 1
1 9 36 84 126 126 84 36 9 1
------------------------------------------------------------
定义int类型数组,长度10,分别赋值随机整数
求所有元素的最大值,最小值,和,平均值,并输出打印
要求随机数都是两位数
public class Test {
public static void main(String[] args) {
//定义数组
int[] arr = new int[10];
//循环赋值
for (int i = 0; i < arr.length; i++) {
//Math.random()默认0.0-1.0,需求是10-99,左闭右开
arr[i] = (int) (Math.random() * 90) + 10;
}
//给数组排序
Arrays.sort(arr);
//全部打印输出
System.out.println(Arrays.toString(arr));
int sum = 0;
//遍历数组
for (int i = 0; i < arr.length; i++) {
//第一个元素就是最小值
if (i == 0){
System.out.println("最小值是:" + arr[i]);
}else if (i == arr.length - 1){//最后个元素就是最大值
System.out.println("最大值是:" + arr[i]);
}
//和
sum += arr[i];
}
System.out.println("和为:" + sum);
System.out.println("平均值为:" + sum/ arr.length);
}
}
---------------------------------------------------
数组的复制
public class Test {
public static void main(String[] args) {
//定义数组
int[] array = {2,3,5,7,11,13,17,19};
//复制数组
int[] arr = new int[array.length];
for (int i = 0; i < array.length; i++) {
arr[i] = array[i];
}
System.out.println(Arrays.toString(arr));
}
}
------------------------------------------------------------------
反转数组
public class Test {
public static void main(String[] args) {
//定义数组
String[] array = {"Aa", "Bb", "Cc", "Dd", "Ee"};
//反转数组
for (int i = 0, j = array.length - 1; i < j; i++, j--) {
String swap = array[i];
array[i] = array[j];
array[j] = swap;
}
//遍历数组
System.out.println(Arrays.toString(array));
}
}
-------------------------------------------------------
二分法查找:
前提:数组是有序的
public class Test {
public static void main(String[] args) {
//定义数组
int[] array = {1, 2, 3, 5, 8, 9, 11, 55, 66};
//找出元素9
int result = 9;
result = 12;
//起始位
int start = 0;
//结束位
int end = array.length - 1;
boolean flag = true;
while (start <= end) {
//中间位
int middle = (start + end) / 2;
if (result == array[middle]) {
System.out.println("恭喜找到了!");
flag = false;
break;
} else if (result > array[middle]) {
start = middle + 1;
} else if (result < array[middle]) {
end = middle - 1;
}
}
if (flag){
System.out.println("没有这个数!");
}
}
}
-------------------------------------------
冒泡排序:相邻数字比较,交换位置
public class Test {
public static void main(String[] args) {
//定义数组,长度7
int[] array = {9, 1, 2, 8, 3, 7, 6};
for (int i = 0; i < array.length - 1; i++) {//外层控制比较次数
for (int j = 0; j < array.length - 1 - i; j++) {//内循环比较大小
if (array[j] > array[j + 1]){
int swap = array[j];
array[j] = array[j + 1];
array[j + 1] = swap;
}
}
}
System.out.println(Arrays.toString(array));
}
}
面向对象题
面向对象思想编程内容的三条主线分别是什么?
1.类的组成:属性,方法,构造器,代码块,内部类
2.三大特征:封装,继承,多态
3.其他关键字:this,super,static,final,abstract,interface,package,import
谈谈你对面向对象中的类和对象的理解,并指出两者关系?
类:是程序员根据现实的事务所虚拟抽象出来的模板
对象:是现实中实际存在的个体,把世界分成不同的单元,每个单元都是独立的个体,多个个体组成起来形成功能系统
类实例化对象,对象抽象成类
面向对象思想的体现?
1.创建类
2.实例化对象
3.通过对象调用类的属性和方法
什么是方法的重载?
同一类体中,方法名相同,参数列表不同
Java方法的参数传递机制的具体体现?
基本数据类型:传递的是数据值
引用数据类型:传递的是地址值
构造器的作用?
创建对象并给实例变量赋值
类的属性赋值方式有几种,先后顺序是怎样的?
5种,默认初始化,显示初始化/代码块赋值,构造器初始化,对象.属性赋值
this关键字可以调用哪些结构?
属性,方法,其他构造器
四种权限修饰符是什么,并说明权限范围?
private:同类
缺省:同包,同类
protected:同包,同类,子类
public:同包,同类,子类,其他包
什么是多态性?什么是虚拟方法的调用?
1.父类引用指向子类对象
2.运行时调用的是子类重写的方法
方法重写的具体规则?
权限修饰符子类 >= 父类,返回值类型子类 <= 父类,方法名相同,参数列表相同,抛出的异常范围子类 <= 父类
super调用构造器需要注意什么?
super只能出现在构造器的首行,不能和this共存
如何实现向下转型,需要注意什么问题,如何解决问题
使用强转符(),可能会发生类型转换异常,用instanceof判断之后在向下转型
==和equals的区别
:可以比较基本数据类型和引用数据类型,基本数据类型比较的是数据值,引用类型比较的是地址值
equals:用来比较引用数据类型,底层用的是,重写前比较的是地址值,重写后比较的是具体内容
写出八大基本类型对应的包装类
byte,short,int,long,float,double,boolean,char
Byte,Short,Integer,Long,Float,Double,Boolean,Character
基本数据类型,包装类,String类型三者之间的转换
基本数据类型 和 包装类,自动转换
基本数据类型(包装类) ==> String类型:用String.valueOf()或者加空串拼接
String类型 ==> 基本数据类型(包装类):XXX.parseXX("…")
static修饰的属性,跟实例变量有什么区别,至少三点
1.属性会随着类的加载而加载
2.保存的位置在方法区的静态域中
3.可以通过类名直接调用
4.静态属性无法声明在方法内
final可以修饰哪些结构,分别表示什么意思
final修饰的类无法被继承
final修饰的变量变成常量,必须赋值,并且无法修改
final修饰的方法无法被重写
final可以声明在方法内
代码实现单例模式的饿汉式和懒汉式
abstract能修饰哪些结构,有什么特点
abstract修饰的类是抽象类,无法实例化对象,提供构造器给子类创建对象
abstract修饰的方法是抽象方法,只有方法声明没有方法体,用来给子类重写
不能修饰变量
抽象类和接口的区别
抽象类:abstract修饰,有构造器,无法实例化,可以有普通方法,成员变量,类只支持单继承
接口:interface修饰,无构造器,无法实例化,只能定义静态常量和抽象方法,接口之间支持多继承,JDK之后可以定义静态方法和默认方法,有方法体
如何创建静态内部类和非静态内部类对象
class A{class B{}}
静态内部类对象:A.B v = A.new B();
非静态内部类对象:A.B v = new A().new B();
---------------------------------------------------------
定义Student类,三个属性:number(int)是学号,state(int)是年级,score(int)是成绩
创建20个学生对象,学号为1-20,年级和成绩由随机数确定
提示:生成随机数:Math.Random();返回double
四舍五入取整:Math.round(double);返回long
1.打印出3年级(state = 3)的学生信息
2.使用冒泡排序按学生成绩排序,并遍历所有学生信息
public class Test {
public static void main(String[] args) {
//创建数组长度20
Student[] students = new Student[20];
//遍历数组赋值
getStudents(students);
//遍历数组
for (int i = 0; i < students.length; i++) {
System.out.println(students[i].toString());
}
System.out.println("===========================");
//打印出3年级(state = 3)的学生信息
getState(students, 3);
System.out.println("===========================");
//使用冒泡排序按学生成绩排序,并遍历所有学生信息
getScore(students);
}
//使用冒泡排序按学生成绩排序,并遍历所有学生信息
public static void getScore(Student[] students) {
for (int i = 0; i < students.length - 1; i++) {//比较19次
for (int j = 0; j < students.length - 1 - i; j++) {
if (students[j].score > students[j + 1].score) {
//必须调用的是属性才能交换,调用getter方法无效!!!
int swap = students[j].score;
students[j].score = students[j + 1].score;
students[j + 1].score = swap;
}
}
}
//遍历数组
for (int i = 0; i < students.length; i++) {
System.out.println(students[i].toString());
}
}
//打印出3年级(state = 3)的学生信息
public static void getState(Student[] students, int state) {
for (int i = 0; i < students.length; i++) {
if (students[i].getState() == state) {
System.out.println(students[i].toString());
}
}
}
//数组遍历赋值
public static Student[] getStudents(Student[] students) {
for (int i = 0; i < students.length; i++) {
int state = (int) (Math.random() * 6) + 1;
int score = (int) (Math.random() * 101);
students[i] = new Student((i + 1), state, score);
}
return students;
}
}
class Student {
private int number;//学号
private int state;//年级
int score;//成绩
public Student(int number, int state, int score) {
this.number = number;
this.state = state;
this.score = score;
}
@Override
public String toString() {
return "Student{" +
"number=" + number +
", state=" + state +
", score=" + score +
'}';
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
public int getState() {
return state;
}
public void setScore(int score) {
this.score = score;
}
}
-----------------------------------------------------------
画出以下方法的内存结构图
public class Test {
public static void main(String[] args) {
Test test = new Test();
test.method01();
}
public void method01(){
int i = 5;
Value v = new Value();
v.i = 25;
method02(v,i);
System.out.println(v.i);
}
private void method02(Value v, int i) {
i = 0;
v.i = 20;
Value val = new Value();
v = val;
System.out.println(v.i + " " + i);
}
}
class Value{
int i = 15;
}
----------------------------------------------------------
定义Cricle类,double类型是属性radius是圆的半径,findArea方法是圆的面积,定义PassObject类,定义方法printAreas,具体是public void printAreas(Cracle c,int time),在printAreas方法打印输出1到time之间的每个整数半径值,以及对应的面积,比如time = 5,则输出半径1,2,3,4,5以及对应的面积,最后在main方法调用printAreas方法,输出当前半径值和面积
public class Test {
public static void main(String[] args) {
//创建对象
RassObject rassObject = new RassObject();
//调用方法
rassObject.printAreas(new Circle(),5);
}
}
class Circle {
double radius;//半径
/**
* 圆的半径
*/
public double findArea() {
return Math.PI * radius * radius;
}
}
class RassObject {
public void printAreas(Circle c, int time) {
for (int i = 1; i <= time; i++) {
c.radius = i;
System.out.println("圆的半径为:" + i + "\t圆的面积为:" + c.findArea());
}
}
}
结果:
圆的半径为:1 圆的面积为:3.141592653589793
圆的半径为:2 圆的面积为:12.566370614359172
圆的半径为:3 圆的面积为:28.274333882308138
圆的半径为:4 圆的面积为:50.26548245743669
圆的半径为:5 圆的面积为:78.53981633974483
-----------------------------------------------------
定义类Account模拟账户,属性:id账号,balance余额,annualInterestRate年利率,方法:getter和setter,返回月利率方法getMonthlyInterest,取款方法withdraw,存款方法deposit,测试方法里创建账号为1122,余额为20000,年利率4.5%的Account对象,使用withdraw方法提款30000,并打印余额,再次提款2500,使用deposit方法存款3000,打印余额和月利率
提示:提款方法withdraw,需判断用户余额是否满足提款要求
创建类CheckAccount代表可透支账户继承账户类,定义属性overdraft代表可透支限额,重写withdraw方法,算法为:如果取款金额小于余额,直接取,如果取款金额大于余额,计算需要透支的额度,判断可透支额度overdraft是否足够支付本次透支需要,如果可以将余额修改为0,冲减可透金额,如果不可以,提示用户超过可透支的限额
测试类创建账号1122,余额20000,年利率4.5%,可透支5000
使用withdraw方法提款5000,打印余额和可透支金额
在使用withdraw提款18000,打印余额和可透支金额
在使用withdraw提款3000,打印余额和可透支金额
public class Test {
public static void main(String[] args) {
//创建账号
Account account = new Account(1122, 20000, 0.045);
//取款30000
account.withdraw(30000);//余额不足,取款失败,当前余额为:20000.0
//取款2500
account.withdraw(2500);
//存款3000
account.deposit(3000);//存款3000.0成功,当前余额为:20500.0 月利率为:0.00375
System.out.println("----------------------");
//创建子类对象
CheckAccount checkAccount = new CheckAccount(1122, 20000, 0.045, 5000);
checkAccount.withdraw(5000);
checkAccount.withdraw(18000);
checkAccount.withdraw(3000);
}
}
//账户类
class Account {
private int id;//账号
private double balance;//余额
private double annualInterestRate;//年利率
/**
* 构造器
*
* @param id 账号
* @param balance 余额
* @param annualInterestRate 年利率
*/
public Account(int id, double balance, double annualInterestRate) {
this.id = id;
this.balance = balance;
this.annualInterestRate = annualInterestRate;
}
/**
* 获取月利率
*/
public double getMonthlyInterest() {
return annualInterestRate / 12;
}
/**
* 取款
*/
public void withdraw(double money) {
//取款金额大于余额
if (money > this.balance) {
System.out.println("余额不足,取款失败,当前余额为:" + getBalance());
return;
}
//更新余额
setBalance(this.balance -= money);
}
/**
* 存款
*/
public void deposit(double money) {
if (money > 0) {
setBalance(this.balance += money);
System.out.println("存款" + money + "成功,当前余额为:" + getBalance() +
"\t月利率为:" + getMonthlyInterest());
}
}
//getter and setter
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
public double getAnnualInterestRate() {
return annualInterestRate;
}
public void setAnnualInterestRate(double annualInterestRate) {
this.annualInterestRate = annualInterestRate;
}
}
/**
* 可透支账户
*/
class CheckAccount extends Account {
private double overdraft;//可透支限额
public CheckAccount(int id, double balance, double annualInterestRate, double overdraft) {
super(id, balance, annualInterestRate);
this.overdraft = overdraft;
}
/**
* 子类取款方法
* @param money
*/
@Override
public void withdraw(double money) {
//余额足够
if (money <= getBalance() && money > 0) {
setBalance(getBalance() - money);
System.out.println("取款" + money + "成功,余额为:" + getBalance() + ",可透支金额为:" + overdraft);
//需要透支消费
} else if (money > getBalance() && money <= getBalance() + overdraft) {
overdraft = this.overdraft - (money - getBalance());
System.out.println("取款" + money + "成功," + "可透支金额剩余:" + overdraft);
setBalance(0);
} else {
//透支了钱也不够
System.out.println("超过可透支限额!");
}
}
}
结果:
余额不足,取款失败,当前余额为:20000.0
存款3000.0成功,当前余额为:20500.0 月利率为:0.00375
----------------------
取款5000.0成功,余额为:15000.0,可透支金额为:5000.0
取款18000.0成功,可透支金额剩余:2000.0
超过可透支限额!
---------------------------------------------------------------------
创建类Order,属性int orderId,String orderName,方法:getter和setter,构造器,重写父类equals方法,测试类创建两个对象,判断创建的两个对象内容是否相等
public class Test {
public static void main(String[] args) {
Order order1 = new Order(1, "1");
Order order2 = new Order(1, "1");
System.out.println(order1.equals(order2));
}
}
class Order {
private int orderId;
private String orderName;
public Order(int orderId, String orderName) {
this.orderId = orderId;
this.orderName = orderName;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Order order = (Order) o;
return orderId == order.orderId && Objects.equals(orderName, order.orderName);
}
@Override
public int hashCode() {
return Objects.hash(orderId, orderName);
}
public int getOrderId() {
return orderId;
}
public void setOrderId(int orderId) {
this.orderId = orderId;
}
public String getOrderName() {
return orderName;
}
public void setOrderName(String orderName) {
this.orderName = orderName;
}
}
结果:true
------------------------------------------------------
利用Vector代替数组,从键盘读入学生成绩(以负数代表结束),找出最高分,并输出学生成绩等级
创建Vector对象,添加元素v.addElement(obj),获取元素v.elementAt(0),计算长度v.size()
若与最高分相差10分内A等,20分内B等,30分内C等,其他D等
public class Test {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
Vector v = new Vector();
int maxValue = 0;
while (true) {
System.out.println("从键盘读入学生成绩(以负数代表结束)");
int score = scanner.nextInt();
if (score < 0) {
break;
}
if (score > 100) {
System.out.println("输入的数据非法!");
continue;
}
//添加成绩
v.addElement(score);//自动装箱成对象
//获取最大值
if (maxValue < score) {
maxValue = score;
}
}
//等级
char grade;
//遍历,比较值
for (int i = 0; i < v.size(); i++) {
Object o = v.elementAt(i);
//转换成int
int score = (int) o;
if (maxValue - score <= 10) {
grade = 'A';
} else if (maxValue - score <= 20) {
grade = 'B';
} else if (maxValue - score <= 30) {
grade = 'C';
} else {
grade = 'D';
}
System.out.println("长度为:" + v.size() + "最大值为:" + maxValue);
System.out.println("student-" + i + ": score is " + score + ",grade is " + grade);
}
}
}
---------------------------------------------------------
定义EcmDef类,接收命令行两个参数,要求不能输入负数,计算两数相除,对数据类型不一致抛出NumberFormatException,缺少命令行参数抛出ArrayIndexOutOfBoundsException,除0ArithmeticException,输入负数定义自定义异常EcDef进行异常处理,在主类EcmDef定义异常方法ecm完成两数相除功能,在main方法使用异常处理语句进行异常处理
public class Test {
public static void main(String[] args) {
try {
//将字符串args解析成int
int result1 = Integer.parseInt(args[0]);
int result2 = Integer.parseInt(args[1]);
System.out.println(EcmDef.ecm(result1, result2));
} catch (NumberFormatException e) {
System.out.println("数据类型不一致");
}catch (ArrayIndexOutOfBoundsException e){
System.out.println("缺少命令行参数");
}catch (ArithmeticException e){
System.out.println("除0");
}catch (EcDef e){
e.getMessage();
}
}
}
class EcmDef {
public static int ecm(int i, int j) throws EcDef{
if (i < 0 || j < 0){
throw new EcDef("不能输入负数!");
}
return i / j;
}
}
//自定义异常类
class EcDef extends Exception {
static final long serialVersionUID = 1L;
public EcDef(){
}
public EcDef(String message){
super(message);
}
}
多线程习题
谈谈你对程序,进程,线程的理解
程序:一段静态代码
进程:一个正在进行的应用程序
线程:进程的一个执行场景
什么场景下存在线程安全问题,怎么解决?
多线程环境下对共享的数据进行了修改操作
使用同步机制
画图说明线程的生命周期,以及切换状态使用的方法
新建:new线程对象
就绪:调用start方法,抢CPU时间片
运行:调用run方法执行
阻塞:sleep,wait方法,进入阻塞状态
消亡:run方法执行结束
谈谈同步代码块中涉及到的同步监视器和共享数据
同步监视器:俗称同步锁,锁的对象必须是多线程共享并且唯一
共享数据:多线程共同操作的数据
同步代码块:包含的是多线程共同操作数据的代码
sleep()和wait()的区别
sleep:是Thread的方法,不会释放对象锁,进入阻塞状态,线程进入一定时间休眠,休眠时间结束后,进入就绪状态抢CPU时间片,获取执行权进入运行状态直至消亡
wait:是Object的方法,释放对象锁后进入阻塞状态,让线程进入无期限等待,直到notify唤醒
创建多线程有几种方式
1.继承Thread
2.实现Runnable
3.实现Callable
4.线程池
------------------------------------------------------
创建两个分支线程,一个遍历100以内的偶数,一个遍历100以内的奇数
public class Test {
public static void main(String[] args) {
//创建两个分支线程,一个遍历100以内的偶数,一个遍历100以内的奇数
Thread thread = new Thread(new MyThread());
Thread thread2 = new Thread(new MyThread2());
thread.setName("t1线程:");
thread2.setName("t2线程:");
thread.start();
thread2.start();
}
}
class MyThread implements Runnable{
@Override
public void run() {
for (int i = 0; i < 101; i++) {
if (i % 2 == 0){
//偶数
System.out.println(Thread.currentThread().getName() + "输出偶数==>" + i);
}
}
}
}
class MyThread2 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 101; i++) {
if (i % 2 != 0){
//奇数
System.out.println(Thread.currentThread().getName() + "输出奇数==>" + i);
}
}
}
}
--------------------------------------------------------------
两个顾客分别向同一账户存3000,每次存1000,存三次,每次存完打印账户余额
public class Test {
public static void main(String[] args) {
//两个顾客分别向同一账户存3000,每次存1000,存三次,每次存完打印账户余额
//创建账户
Account account = new Account(0.0);
//创建顾客,共享同一账户
Thread thread1 = new Thread(new Customer(account));
Thread thread2 = new Thread(new Customer(account));
thread1.setName("t1线程:");
thread2.setName("t2线程:");
thread1.start();
thread2.start();
}
}
//账户类
class Account {
private double balance;//余额
/**
* 存钱
* @param
*/
public synchronized void addMoney(int money){
if (money > 0){
setBalance(getBalance() + money);
System.out.println(Thread.currentThread().getName() + "存入" + money + "成功,余额为:" + getBalance());
}
}
public Account(double balance) {
this.balance = balance;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
}
//顾客类
class Customer implements Runnable{
private Account account;//账户
public Customer(Account account) {
this.account = account;
}
@Override
public void run() {
//存三次
for (int i = 0; i < 3; i++) {
account.addMoney(1000);
}
}
}
结果:
t1线程:存入1000成功,余额为:1000.0
t1线程:存入1000成功,余额为:2000.0
t1线程:存入1000成功,余额为:3000.0
t2线程:存入1000成功,余额为:4000.0
t2线程:存入1000成功,余额为:5000.0
t2线程:存入1000成功,余额为:6000.0
---------------------------------------------------------------
生产者Producer将产品交给商店Clerk,消费者Customer从商店处取走商品,店员一次只能持有固定产品数比如20,如果生产者想继续生产,商店会叫停,如果有空位了再叫生产者生产,如果商店没有产品了,商店会叫消费者等待,如果商店有商品了再叫消费者取走商品
public class Test {
public static void main(String[] args) {
//创建店员对象
Clerk clerk = new Clerk();
Producer p1 = new Producer(clerk);
Customer p2 = new Customer(clerk);
p1.setName("生产者1号:");
p2.setName("消费者2号:");
p1.start();
p2.start();
}
}
//生产者
class Producer extends Thread {
private Clerk clerk;//关联商店
public Producer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "开始生产......");
while (true) {
clerk.product();//生产之后给商店
}
}
}
//商店
class Clerk {
private int number = 0;//商品的数量
//进货产品
public synchronized void product() {//锁是this,当前对象Clerk,Clerk只创建一个满足唯一
if (number < 20) {
//进货
number++;
System.out.println(Thread.currentThread().getName() + "开始进货第" + number + "个商品!");
//消费不满足20个就可以继续生产
notify();
} else {
//有20个商品了,停止进货
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//出售产品
public synchronized void consume() {
if (number > 0) {
//出售
System.out.println(Thread.currentThread().getName() + "开始消费第" + number + "个商品!!!");
number--;
//唤醒
notify();
} else {
//等待有货
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//消费者
class Customer extends Thread {
private Clerk clerk;//关联商店
public Customer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "开始消费......");
while (true) {
clerk.consume();
}
}
}
结果:死循环
常用类习题
String,StringBuffer,StringBuilder三者区别
String:一旦创建不可改变,不适合做大量字符串拼接,存储在方法区的字符串常量池中,JDK9之后底层是byte数组,相较于char数组,内存更小
StringBuffer:是可变序列,线程安全,效率高于String,低于StringBuilder,适用于做大量字符串拼接操作
StringBuilder:是可变序列,非线程安全,效率最高,适用于做大量字符串拼接操作
什么是枚举类,枚举对象的声明修饰符有哪些
1.枚举类型对象是可数的,确定的
2.只能是public static final修饰
什么是元注解,Retention和target的作用
1.对注解进行解释说明的注解就是元注解
2.Retention:描述注解的生命周期,SOURCE:Java源文件,CLASS字节码文件,RUNTIME字节码文件并且能被反射
3.target:描述注解出现的位置,类上,属性上,方法上…
throw和throws的区别
throw:手动创建异常对象并抛出,出现在方法体内
throws:处理异常的方式之一,声明抛出,出现在方法声明最后
-----------------------------------------------------------------
将字符串"2020-09-08"转换为java.sql.Date
public class Test {
public static void main(String[] args) {
//将字符串"2020-09-08"转换为java.sql.Date
String str = "2020-09-08";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
try {
Date parseDate = sdf.parse(str);
java.sql.Date sqlDate = new java.sql.Date(parseDate.getTime());
System.out.println("sqlDate = " + sqlDate);
} catch (ParseException e) {
e.printStackTrace();
}
}
}
集合练习题
集合Collection中存储的如果是自定义的类,需要自定义重写哪个方法,为什么?
equals方法,因为contains,remove方法底层比较的是对象的内容
ArrayList,LinkedList,Vector三者的区别
三者都是List接口的实现类,是有序可重复的
ArrayList:底层是数组结构,非线程安全,查询效率高,通过数组首元素下标计算其他元素下标偏移量快速定位,增删效率低,首尾元素不受影响,如果增加或者删除数组中某个元素,元素后面的其他元素下标全会受牵连,
调用add方法底层才会创建长度为10的数组,扩容为原数组的1.5倍
LinkedList:底层是双向链表,非线程安全,查询效率低,通过首节点元素的下标依次遍历数组中的元素,直到找到该元素,增删效率高,如果增加或者删除链表中的某个元素,其他元素不受影响
Vector:底层是数组结构,线程安全,效率低,底层创建长度为10的数组,扩容为原数组的2倍
List接口的常用方法,增删改查,插,长度,遍历
add添加/插入,remove删除,set修改,get查询,size长度,iterator遍历器
set存储数据的特点是什么,常见的实现类有哪些
set存储元素特点:单列集合,是无序不可重复的
实现类:HashSet,LinkedHashSet,TreeSet
Map集合存储数据的特点是什么,并指明key,value,entry存储数据的特点
双列集合,以键值对方式存储元素
key:无序不可重复
value:无序可重复
entry:无序不可重复
描述HashMap的底层原理
HashMap底层是哈希表,数组 + 链表 + 红黑树结构,添加元素时,类调用HashCode方法计算哈希值定位下标,如果下标处没有元素,则直接存储元素,有元素,则调用equals方法,返回false,则直接存储元素,返回true,则存储失败
调用put方法才会创建数组长度为16的数组,加载因子为0.75f,当链表元素个数 > 8,数组长度 > 64,则转换成红黑树结构
Map常用类有哪些,有什么特点
HashMap,HashTable,TreeMap
HashMap:非线程安全的,效率高,可以存储null的key和value,子类LinkedHashMap,可以按照添加元素的顺序遍历集合
HashTable:线程安全的,效率低,不能存储null的key和value,子类Properties,常用于配置文件,只能存储String类型
TreeMap:底层是红黑树,比较器Comparator默认从小到大排列
Collection和Collections的区别
Collection:是单列集合,子接口有List,Set
Collections:是操控Collection和Map的工具类
如何遍历Map的key集,value集,key-value集,使用上泛型
提供方法用于遍历HashMap<String,String>中所有的key,存入List返回,使用上泛型
Map常用方法有哪些
put,remove,put改,get,size,遍历
------------------------------------------------------------------
分析以下set集合能否存储成功
public class Test {
public static void main(String[] args) {
HashSet set = new HashSet();
Person p1 = new Person(1, "AA");
Person p2 = new Person(2, "BB");
set.add(p1);
set.add(p2);
System.out.println(set);
p1.name = "CC";
set.remove(p1);//没有1,"CC"对应的HashCode值
System.out.println(set);
set.add(new Person(1, "AA"));//该位置的AA已被修改,所以AA可以存储
System.out.println(set);
set.add(new Person(1,"CC"));//集合里还没有CC的HashCode值,所以能存储
System.out.println(set);
}
}
class Person{
int id;
String name;
@Override
public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
public Person(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return id == person.id && Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(id, name);
}
}
结果:
[Person{id=1, name='AA'}, Person{id=2, name='BB'}]
[Person{id=1, name='CC'}, Person{id=2, name='BB'}]
[Person{id=1, name='CC'}, Person{id=1, name='AA'}, Person{id=2, name='BB'}]
[Person{id=1, name='CC'}, Person{id=1, name='AA'}, Person{id=1, name='CC'}, Person{id=2, name='BB'}]
IO流练习题
流的三种分类方式
流向:输入流,输出流
数据单位:字节流,字符流
流的角色:节点流,处理流
写出4个IO流中的抽象类,4个文件流,4个缓冲流
InputStream,OutInputStream,Reader,Writer
FileInputStream,FileOutInputStream,FileReader,FileWriter
BufferedInputStream,BufferedOutInputStream,BufferedReader,BufferedWriter
字节流和字符流的使用场景
字节流:处理非文本文件
字符流:处理文本文件
什么是URL,举个例子
URL:是统一资源定位符
new URL(“http://localhost:8080/projectName/userLogin?username=Tom&passworld=123”)
谈谈你对对象序列化机制的理解
序列化过程:内存数据传输到磁盘的过程就是序列化
反序列化:磁盘中的数据传输到内存中就是反序列化
对象实例化需要满足的条件
1.实现Serializable接口
2.提供序列化版本号
3.要求对象属性也是可序列化的
使用缓冲流实现a.jpg文件复制为b.jpg文件的操作
转换流是哪两类,作用是什么
InputStreamReader:将字节输入流转换成字符输入流,解码过程
OutputStreamWriter:将字节输出流转换成字符输出流,编码过程
创建一个与a.txt文件同目录下的另一个文件b.txt
复制某目录下的所有资源到另一目录
网络编程题
IP对应的类是什么,实例化类的方式是什么,两个常用方法是什么
InetAddress
InetAddress.getByName(String host);,InetAddress.getLocalHost();获取本地ip
getHostName(),getHostAddress()
传输层的TCP协议和UDP协议主要区别
TCP:可靠的数据传输(三次握手),进行大数据量的传输,效率高
UDP:不可靠,以数据报形式发送,限为64K,效率低
反射练习题
写出获取Class实例的三种方式
1.Class.forName(“全路径”)
2.类名.class
3.引用.getClass()
谈谈你对Class类的理解
Class实例对应这加载到内存中的一个运行时类
创建Class对应运行时类的对象的通用方法,代码实现,构造器方面需要满足什么要求
newInstance方法,创建了对应的运行时类的对象
必须有无参构造器,权限修饰符的权限要够