1.接口
是一种特殊抽象类.也是一种引用数据类型.
1.1接口的本质
是一种约定,一种标准.表示是一种扩展功能.
1.2:接口的关键字:
- 声明接口:interface
- 实现接口:implements
1.3:接口的特征:
- 1:接口不能实例化,接口中没有构造方法.
- 2:接口中方法全是抽象方法.(注意:在JDK1.8之后,接口中可以有静态方法和default修饰的方法必须有方法体)接口中变量全是静态常量.
- 3:接口中所有抽象方法不用abstract修饰,系统会默认给接口中方法加public abstract.接口中所有变量全是静态常量,所以接口中所有变量,系统会默认给它加public static final修饰.
- 4:接口中所有抽象方法都要被子类重写,除非子类是抽象的,那就后代类去实现.
- 5:一个类如果既有父类又有父接口,先继承父类再实现父接口.
- 6:一个类可以实现多个接口,一个接口可以继承多个接口,所以类与接口是实现关系,接口与接口是继承关系.
- 7:一个类只能直接继承一个父类,但是可以实现多个接口,所以从某种程度上来说接口弥补java单继承的缺陷.
1.4:接口的作用
增加程序的可扩展性.
- 让设计与实现分离.
- 更好使用多态.
- 降低程序耦合度.(耦合度意思就是依赖性)
/**
* 宠物类
*/
public abstract class Pet{
public String nickName;
public abstract void eat() ;
}
/**
* 接口
*/
public interface IclimbTree {
public void climbTree();
}
public class Cat extends Pet implements IclimbTree,IcatchMouse{
@Override
public void eat() {
System.out.println("猫在吃鱼");
}
@Override
public void climbTree() {
System.out.println("猫在爬树");
}
@Override
public void catchMouse() {
System.out.println("猫抓老鼠");
}
}
2 (扩展)接口的分类:
- 普通接口:接口中可以抽象方法,也可以有静态方法,还可以静态常量.
- 常量群接口:接口中只有静态常量.
- 标志性接口:接口中没有方法及常量,是空的.它只标记一种特定功能.eg:Serializable
3.抽象类 VS 接口
3.1:相同点:
- 1:都不能实例化.
- 2:都可以声明抽象方法和静态常量.
- 3:他们的抽象方法都必须被子类重写,除非子类也是抽象的.
- 4:他们都可以编译后生成一个字节码文件(.class文件).
3.2:不同点:
- 1:作用不同:抽象类规定子类必须拥有行为(表示子类必须拥有的公共属性和方法);
接口表示的是一种扩展功能. - 2:方法不同:抽象类中可以有抽象方法也可以实例方法,只是抽象类中抽象方法必须 用abstract;接口中方法默认都是抽象方法,不用显示写abstract修饰, 在jdk1.8之后接口还可以有静态方法和默认方法.
- 3:常量不同:抽象类中可以有变量也可以有常量;接口中只能有静态常量.
- 4:构造方法不同:抽象类中有构造方法,专门用来供子类调用;接口中没有构造方法.
- 5:与类的关系不同:一个类只能继承一个抽象类;但是一个类可以实现多个接口.
一个抽象类能继承一个类也能实现接口;一个接口可以继承多个 接口. - 6:代码块不同:抽象类中可以动态和静态代码块;接口中没有动态和静态代码块.
- 7:方法修饰符不同:抽象类中方法可以使用任意访问修饰符;接口中所有方法默认是public.
4.扩展方法
JDK1.8后,接口中增加静态方法的用default修饰的扩展方法.
- 1:静态方法增加程序灵活性;
- 2:扩展方法提高程序可维护性;
- 3:接口中静态方法,子类不能继承,但是可以通过接口名调用.
- 4:接口中扩展方法,子类可以直接继承过去.
- 5:如果一个子类的父接口和父类中有相同的方法,子类继承或重写的是父类中的方法.
(方法相同时,类优先) - 6:当一个类的两个父接口,且两个父接口中有相同的方法,子类必须重写方法以解决冲突.
5.设计原则
根据门的故事总一个Java设计的原则:
接口隔离原则(ISP):一个接口只被设计一种功能.
6.常用类
6.1.内部类
在一个类里面再声明一个类.
6.1.1 内部类的分类
成员内部类,成员静态内部类,局部内部类(匿名内部类)
注意: 成员内部类可以用访问修饰符;局部内部类不能用访问修饰符.
6.1.2:成员内部类
直接在类中与成员变量和方法并列的位置声明的类.
- 1:外部类中想调用成员内部类的方法和属性必须用内部类的对象调用.
- 2:成员内部类中可以直接调用外部类的属性和方法.
- 3:当外部类的属性与内部类的属性同名时,在内部类中使用属性时,内部类的属性的优先级更高.
- 4.当外部类的属性与内部类的属性同名时,想在内部类中调用外部类的属性:
- 5.内部类(非静态)不可以使用静态代码块,但是可以使用静态常量
外部类名.this.属性名;
外部类的对象.属性名;
/**
* 外部类
*/
public class Outer {
/ **
* 外部类属性
*/
private String name="小外";
/**
* 外部类的方法
*/
private void show() {
System.out.println("这是外部类的一个方法");
}
/**
* 外部类中计算两数之和的方法
* @param num1
* @param num2
* @return double
*/
public double add(double num1,double num2) {
//外部类中想调用成员内部类的方法和属性必须用内部类的对象调用
//创建内部类的对象
Inter in1=new Inter();
System.out.println(in1.a);
//用内部类调用方法并返回结果
return in1.add2(num1, num2);
}
/**
* 成员内部类
*/
public class Inter{
public int a;
public String name="小花";
/**
* 内部类中计算两数之和的方法
* @param num1
* @param num2
* @return double
*/
public double add2(double num1,double num2) {
return num1+num2;
}
public void m1() {
//成员内部类中可以直接调用外部类的成员变量和方法
// System.out.println("外部类的属性:"+name);
// show();
//当外部类的属性名与内部类属性名同名时,内部类优先级更高.
System.out.println("姓名:"+name);//内部类属性
//在内部类中调用外部类属性或方法
System.out.println("外部类中姓名属性为:"+Outer.this.name);
System.out.println("外部类中姓名属性为:"+new Outer().name);
}
}
}
public static void main(String[] args) {
//创建外部类的对象
Outer ou1=new Outer();
//用外部类的对象调用外部类中方法
double result1=ou1.add(11, 22);
System.out.println("两数之和为:"+result1);
//创建成员内部类的对象
//Outer.Inter in2=new Outer().new Inter();
Outer.Inter in2=ou1.new Inter();
//用内部类的对象调用内部类的方法
double result2= in2.add2(33, 44);
System.out.println("两数之和为:"+result2);
in2.m1();
}
6.1.3:成员静态内部类
直接在类中与成员变量和方法并列的位置声明的static类.
- 1:在成员静态内部类中可以直接调用外部类的静态属性和方法;不可以直接调用外部类非静态属性和方法,如果想调用外部类的非静态属性和方法可能通过外部类的 对象调用.
- 2:在外部类中可以用成员静态内部类的类名直接调用内部类的静态属性和方法,非静态的属性和方法只能通过成员静态内部类的对象调用.
public static void main(String[] args) {
//创建外部类的对象
Outer2 ou2=new Outer2();
//用对象调用方法
ou2.show1();
//创建成员静态内部类的对象
Outer2.Inter2 in2=new Outer2.Inter2();
in2.m1();
}
6.1.4:局部内部类(了解)
在一个方法中声明的类叫局部内部类.只在当前声明方法中用.
public class Outer3 {
/**
* 外部类的一个方法
* @param num1
* @param num2
* @return
*/
public double add(double num1,double num2) {
//普通局部内部类
class Inter3{
//局部内部类的方法
public double add2(double a,double b) {
return a+b;
}
}
//创建局部内部类的对象
Inter3 in3=new Inter3();
return in3.add2(num1, num2);
}
}
6.1.5:匿名内部类
在方法中声明的一个类且这个类没有类名,用父类或父接口作为类的数据类型.
- 使用场景:如果一个类一生只有一个对象,且这个对象一生只用一次就可以使用匿名内部类.
- 使用:用父类或父接口为作匿名类的类型,在声明匿名类的同时调用匿名类的构造方法创建匿名类对象.注意创建对象时,因为匿名没有类名,所以用父抽象类或父接口作为构造方法数据类型.
- 作用:可以少创建一个java文件来单独声明一个类.
public static void main(String[] args) {
//数据类型 对象名=new 类名();
//对象名.方法名();
//{}是匿名内部类,new IUsb()用父接口作为数据类型调用匿名类构造方法创建这个匿名内部类的对象,work()重写父接口中方法
//用父接口作为数据类型,指向匿名子类对象
IUsb usb1= new IUsb() {
/**
* 重写接口中方法
*/
@Override
public void work() {
System.out.println("usb风扇在吹风");
}
};
usb1.work();
//用父抽象类作为数据类型,创建匿名内部类的对象
Animal a1=new Animal() {
/**
* 重写父抽象类中吃的方法
*/
@Override
public void eat() {
System.out.println("猫在吃鱼");
}
};
//用对象调用方法
a1.eat();
}
6.2 一个Java类对应一个.class字节码文件:
成员内部类.class文件命名:外部类名$内部类名.class
局部内部类.class文件命名:外部类名$编号内部类名.class
匿名内部类.class文件命名:外部类名$编号.class
6.3 Object类:是所有java的根类.所有java类都直接或间接继承Object.
- 1:getClass():获得一个对象运行时类,因为一个类只有一个运行时类,所以可以用方法判断两个对象是否同一个类型的.
- 2:hashCode();得到对象哈希码值,一个对象只有唯一哈希码值,如果两个对象,内存不同,它们哈希码值.
- 3:toString();返回对象字符串表示形式.
- 1)对象调用toString()默认输出对象的内存地址的字符串形式
- 2)如果对象所在的类中重写Object类继承过来toString(),就可以输出对象的值
- 3)java中输出语句默认输出内容时,会自动调用toString();
- 4:finalize();标记当前对象已经用完,可以被回收.
- 5: == VS equals()
- ==:如果==左右两边变量是基本数据类型,比值 ==左右两边是引用数据类型比的就是内存地址.
- equals():如果equals()左右两边的变量所在的类中,没有重写Object类中equals(),equals()作用比两个变量的内存地址.如果equals()左右两边的变量所在的类中重写Object类中equals(),equals()作用比两个变量的值.
注意: String类因为重写Object类的equals(),所以String类所有变量用equals()比较,比值.
public static void main(String[] args) {
int num1=11;
int num2=11;
Student stu1=new Student("张三", 18);
Student stu2=new Student("张三", 18);
//==左右两边是基本数据类型,比值
System.out.println(num1==num2);//true
//==左右两边是引用数据类型,比地址
System.out.println(stu1==stu2);//false
//当Student类中没有重写Object类的equals(),比地址,false
//当Student类中重写Object类的equals(),比值,true
System.out.println(stu1.equals(stu2));
}
6.4 包装类
八种基本数据类型违反了Java是一种纯面向对象语言,所以java给八种基本数据类型提供了包装 类.
- 1:包装类(封装类):封装了基本数据类型的类叫包装类.
- 2:所有包装类都是引用数据类型
- 3:基本数据类型 一一 包装类
byte 一一 java.lang.Byte
short 一一 java.lang.Short
int 一一 java.lang.Integer
long 一一 java.lang.Long
float 一一 java.lang.Float
double 一一 java.lang.Double
char 一一 java.lang.Character
boolean 一一 java.lang.Boolean
6.4.1 基本数据类型与包装类的转换
- 装箱:将基本数据类型转换包装类
- 拆箱:将包装类转换为基本数据类型
- 从jdk1.5之后,Java支持自动装箱和拆箱
public static void main(String[] args) {
int count1=22;
//装箱:将基本数据类型转换包装类
Integer count2=Integer.valueOf(count1);
Integer count3=new Integer(count1);
//拆箱:将包装类转换为基本数据类型
Integer count4=new Integer(33);
int count5=count4.intValue();
//从jdk1.5之后,Java支持自动装箱和拆箱
Integer count6=count1;
Integer count7=33;
int count8=count6;
}
- java中用包装类作为数据类型,原因是包装类有很多方法,方便对变量作操作;包装类作为成员变量的数据类型,默认值都是null,方便判断.
- Integer整数缓冲区:Integer类中封装了一个私有的静态内部类IntegerCache,该类中又定义了一个Integer cache[]的缓冲区,大小其实就是区间[-128, 127]的大小,该区间内的数已经通过实例化操作将各自对应的Integer对象封装在了数组cache中,即Integer cache[0] = new Integer(-128);依次初始化到数字127结束。
(设置整数缓冲区的原因,主要是因为缓冲区的数据在平时是最常用的,多次使用相同的底层对象可以有助于提高底层的优化)
6.5 String类
最终类,字符串不变的类; 它们的值在创建后不能被更改。
注意: 常量池中值不可重复,字符串直接给值,给字符串常量,存在常量池中.
6.5.1:== VS equals()
String类调用==,比地址;String类用equals()比较的是值,因为String类重写Object类equals().
注意: 字符串直接赋值,字符串值是字符串常量,在常量池中,常量池中值不可重复.
public static void main(String[] args) {
String s1="张三";
String s2="张三";
String s3=new String("张三");
String s4=new String("张三");
//==左右两边是String引用数据类型,比地址,s1和s2批向同一个常量池地址
System.out.println(s1==s2); //true
//String重写equals(),比值
System.out.println(s1.equals(s2)); //true
//比地址,地址不同
System.out.println(s1==s3); //false
//比值
System.out.println(s1.equals(s3)); //true
//比地址,两个不同地址
System.out.println(s3==s4); //false
//比值
System.out.println(s3.equals(s4)); //true
}
6.5.2:其他数据类型转换为字符串:
- 1:String.valueOf(Object obj) 可以将其他数据类型变量转换为字符串
- 2:其他数据类型变量+""
- 3:其他数据类型变量.toString()
- 4:byte[],char[]可以通过String的构造方法转换为字符串
- 5:Arrays.toString()可以将任何数组转换字符串
public static void main(String[] args) {
//其他数据类型变量+""
System.out.println(99+"");
//其他数据类型变量.toString()
Integer num1=22;
System.out.println(num1.toString());
//byte[],char[]可以通过String的构造方法转换为字符串
char[] c1= {'a','b'};
String s1=new String(c1,0,c1.length);
System.out.println(s1);
//用Arrays.toString();
String s2=Arrays.toString(c1);
System.out.println(s2);
//String.valueOf()
System.out.println(String.valueOf(true));
}
6.5.3:字符串转换为其他数据类型:
- 其他的数据类型名.parse数据类型(String s);//有风险
- 其他的数据类型名.valueOf(String s);//有风险
//字符串转换为其他类型
String s3="11";
Integer n1=Integer.valueOf(s3);
Integer n2=Integer.parseInt(s3);
6.5.4:"" VS null
- “”:在内存中开辟一块空间,空间上没有存值.
- null:没有开辟空间.
6.5.5:String类常用方法:
public static void main(String[] args) {
//准备一个字符串
String s1="我是程序员,我爱java";
//在java中数组和字符串索引都是从0开始计数
//1.charAt(int index) 返回字符串指定索引处的字符
c1=s1.charAt(7);
//2.compareTo(String anotherString)按字典顺序比较两个字符串顺序
int num1="A".compareTo("a");
//3.compareToIgnoreCase(String str) 按字典顺序忽略大小写比较两个字符串顺序
int num2="A".compareToIgnoreCase("a");
//4.concat(String str) 将指定的字符串连接到该字符串的末尾得到一个新字符串。
String s2=s1.concat("欧耶");
//5.contains(CharSequence s) 判断当前字符串是否包含指定的char值序列时才返回true。
Boolean result1=s1.contains("程序员");
//6.startsWith(String prefix) 测试此字符串是否以指定的前缀开头。
Boolean result2=s1.startsWith("我");
//7.endsWith(String suffix) 测试此字符串是否以指定的后缀结尾。
Boolean result3=s1.endsWith("程序员");
//8.equals(Object anObject) 将此字符串与指定对象进行比较。
Boolean result4="ab".equals("Ab");
//9.equalsIgnoreCase(String anotherString) 将此 String与其他 String比较,忽略大小写。
Boolean result5="ab".equalsIgnoreCase("Ab");
//10.String.format(String format, Object... args) 使用指定的格式字符串和参数返回格式化的字符串。
String s3=String.format("%.2f", 3.1465926);//四舍五入
//在这个方法中%.2f起到两个作用,第一个占位符作用,第二个变量转换的作用
String s4=String.format("张三的成绩为%.2f", 59.19999999999);
//System.out.printf("李四的成绩为%.2f\n", 99.21999999999999999999999);
String s5=String.format("%d%%", 50);
//11.indexOf(String str) 返回指定子字符串第一次出现的字符串内的索引。
Integer index1=s1.indexOf("程序员");
//12.lastIndexOf(String str) 返回指定子字符串最后一次出现的字符串中的索引。
Integer index2=s1.lastIndexOf("程序员");
//13.String.join(CharSequence delimiter, CharSequence... elements) 返回一个新的字符串,由 CharSequence elements的副本组成,并附有指定的delimiter的 delimiter 。
String[] ss= {"1","2","3","4"};
String s6=String.join("#","我是中国人", "我爱中国");
String s7=String.join("", ss);
//14.length() 返回此字符串的长度。
int len1=s1.length();
//15.matches(String regex) 告诉这个字符串是否匹配给定的 regular expression 。
Boolean result6="abc".matches("abc");
//16.replace(CharSequence target, CharSequence replacement) 将与字面目标序列匹配的字符串的每个子字符串替换为指定的字面替换序列。
String s8="这是一本热血的漫画".replace("热血", "**");
//17.split(String regex) 将此字符串分割为给定的 regular expression的匹配。
String[] ss2=s1.split(",");
// for (String st : ss2) {
// System.out.println("##"+st);
// }
//18.substring(int beginIndex, int endIndex) 返回一个字符串,该字符串是此字符串的子字符串。包括起始索引不包括终止索引
String s9=s1.substring(7, 10);
//19.toCharArray() 将此字符串转换为新的字符数组。
char[] cs=s1.toCharArray();
// for (char c : cs) {
// System.out.println(c);
// }
//20.toLowerCase() 将所有在此字符 String使用默认语言环境的规则,以小写。
String s10="abCdEF".toLowerCase();
//21.toUpperCase() 将所有在此字符 String使用默认语言环境的规则大写。
String s11="abCdEF".toUpperCase();
//22.trim() 返回一个字符串,其值为此字符串,并删除任何前导和尾随空格。
String s12=" dfdfef fd ffefdd ".trim();
System.out.println(s12);
}
6.6 StringBuffer
最终类,字符串类.线程安全,可变的字符序列。 字符串缓冲区就像一个String,但可以修改.一般用在多线程中.效率稍低
- StringBuilder:最终类,字符串类.在多线程中不安全,效率高.可变的字符序列。 一般用在单线程中.
6.6.1:常用方法
public static void main(String[] args) {
//创建一个字符串对象
StringBuilder sb1=new StringBuilder();
//1.append(Object obj) 追加 Object参数的字符串 Object形式。
sb1.append("哈哈,我是程序员");
//2.delete(int start, int end) 删除此序列的子字符串中的字符。
sb1.delete(0, 3);
//3.insert(int offset, Object obj) 将 Object参数的字符串表示插入到此字符序列中。
sb1.insert(2, "低调");//从插入位置开始把字符往后移
//4.replace(int start, int end, String str) 用指定的String中的字符替换此序列的子字符串中的 String 。
sb1.replace(2, 4, "低调");
//5.reverse() 导致该字符序列被序列的相反代替。
sb1.reverse();
System.out.println(sb1);
}
6.7.String VS StringBuffer VS StringBuilder(面试常问)
- 相同点:
- 1都可以存字符串;
- 2都是最终类
- 不同点:
- 1.String是最终不可变字符序列.它一但赋值,长度不可变.
- 2.StringBuffer是最终可变字符序列.它的长度可变.线程安全,效率低,一般建议在多线程中用.
- 3.StringBuilder是最终可变字符序列.它的长度可变.效率高,在多线程用不安全一般建议在单线程中使用.
6.8.BigDecimal类:精确计算浮点数.
public static void main(String[] args) {
double num1=1.0;
double num2=0.9;
double num3=num1-num2;
System.out.println(num3);
}
//BigDecimal类的常用方法
public static void main(String[] args) {
//声明两个变量
BigDecimal num1=new BigDecimal(1.0);
BigDecimal num2=new BigDecimal(0.9);
//计算两数之和
BigDecimal result1=num1.add(num2);
System.out.println("两数之和为:"+result1);
//计算两数之差
BigDecimal result2=num1.subtract(num2);
System.out.println("两数之差为:"+result2);
//计算两数之积
BigDecimal result3=num1.multiply(num2);
System.out.println("两数之积为:"+result3);
//计算两数之商,除不断时会报错
//BigDecimal result4=num1.divide(num2);
//计算两数之商,第一个参数是除数,第二个参数是保留的小数位数,第三个舍入方式
BigDecimal result4=num1.divide(num2,2,BigDecimal.ROUND_HALF_UP);
System.out.println("两数之商为:"+result4);
}
6.9:java.util.Date类:日期类
public static void main(String[] args) {
//获得当前系统日期
Date today=new Date();
System.out.println(today);
Date d1=new Date(1000);
//判断当前日期是否在指定日期之前
Boolean result1= today.before(d1);
//判断当前日期是否在指定日期之后
Boolean result2= today.after(d1);
//比较两个日期
int result3=today.compareTo(d1);
System.out.println(result3);
}
6.10:java.text.SimpleDateFormat:字符串与日期的格式化类
- 1:format(Date date) 将日期转换为字符串
- 2:parse(String source) 将符合格式的字符串转换日期
public static void main(String[] args) throws ParseException {
//获得当前系统日期
Date today=new Date();
System.out.println(today);
//创建日期格式化对象
SimpleDateFormat sdf=new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
//将日期转换为指定格式字符串
String s1=sdf.format(today);
System.out.println(s1);
//将字符串转换为指定格式日期,有风险,字符串符合格式化对象内设置格式才能转换为日期
Date d2=sdf.parse("2020年01月01日 11:11:11");
System.out.println(d2);
}
6.11:java.util.Calendar:日期类.
public static void main(String[] args) {
//获得日期对象,用父抽象类作为数据类型,用父抽象类调用静态方法得到子类对象
Calendar c2=Calendar.getInstance();
System.out.println(c2);
System.out.println("年"+c2.get(Calendar.YEAR));
//c2.set(Calendar.MONTH,1);
System.out.println("月"+(c2.get(Calendar.MONTH)+1));
System.out.println("日"+c2.get(Calendar.DAY_OF_MONTH));
System.out.println("时"+c2.get(Calendar.HOUR_OF_DAY));
System.out.println("分"+c2.get(Calendar.MINUTE));
System.out.println("秒"+c2.get(Calendar.SECOND));
System.out.println("全年中月中最大一天:"+c2.getMaximum(Calendar.DATE));
System.out.println("全年中月中最小一天:"+c2.getMinimum(Calendar.DATE));
System.out.println("当前月中最大一天:"+c2.getActualMaximum(Calendar.DATE));
System.out.println("当前月中最小一天:"+c2.getActualMinimum(Calendar.DATE));
}
6.12:java.lang.System:系统类
public static void main(String[] args) {
//System.out.println("111");
//1.退出jvm
//System.exit(0);
//System.out.println("这是一个标准输出流的输出方法");
//System.err.println("这是一个错误输出注的输出方法");
//2.获得系统毫秒数
System.out.println("当前时间毫秒数:"+System.currentTimeMillis());
//3.获得系统纳秒数
System.out.println("当前时间纳秒数:"+System.nanoTime());
//4.获得当前系统所有属性
System.out.println("当前系统所有属性:"+System.getProperties());
//5.获得当前系统指定属性
System.out.println("Java 运行时环境版本:"+System.getProperty("java.version"));
}
6.13:(扩展)java.lang.Math类:对数字作处理.
生成min<=r<=max随机数的公式:int 变量=(int)(Math.random()*(max-min+1)+min);
public static void main(String[] args) {
Double num1=25.6;
//1.向上取整数
Double num2=Math.ceil(num1);
//2.向下取整数
Double num3=Math.floor(num1);
//3.四舍五入
long num4=Math.round(num1);
//4.求一个数几次方
Double num5=Math.pow(2, 3);
//5.求0到1之间随机数,包括0不包括1
Double r1=Math.random();
//生成一个两位随机整数 75~100
int r2=(int)(Math.random()*(100-75+1)+75);
System.out.println(r2);
}
6.14:(扩展)java.util.Random:生成随机数的类
生成min<=r<=max随机数的公式:int 变量=random的对象.nextInt(max-min+1)+min;
//创建随机对象
Random r=new Random();
int r3=r.nextInt(10);
//生成一个两位随机整数 75~100
int r4=r.nextInt(100-75+1)+75;
个人笔记,思路,仅供参考