Java程序设计实践
Java训练集1~3总结与心得
训练集链接
前言: 本次训练集1~3主要考察了对java基础语法的掌握,内容包括基础程序的设计,类设计,编程规则的掌握等等,学习的重点在于适应java的语法以及让类的设计规范合理。
PS:这次Java训练是某种意义上我第一次上手Java,题目难度不大,每道题都有很多方案可以解决,所以,我的这篇博客更多的会去分享一些我认为某种情景下更适用的方法。另外,我是校内ACM社团的一员,经常用C++写算法题,所以我也会去分析二者的同异处。
目录:
- 训练集速析(主要分享多种解题思路)
- 典例精析(详细的介绍设计以及分析过程)
- 踩坑心得(初试java会出现的各种问题)
- 综述(目前对java这门语言的认识)
- 杂谈
训练集速析
训练集一
主要内容:介绍 Java 基础知识,包括数据类型、运算符、流程控制语句、数组等内容。
1.九九乘法口诀表
- 接收用户从键盘输入的一个1到9(含边界)的整数,假设该整数是n,则打印乘法表的前n行。
解析:该题考察的就是java的循环结构,用双重循环可以轻松的实现需求。
需要注意的是:java有另一种不同与C的for循环语法,代码如下:
public class Test {
public static void main(String[] args){
int [] numbers = {10, 20, 30, 40, 50};
for(int x : numbers ){
System.out.print( x );
System.out.print(",");
}
System.out.print("\n");
String [] names ={"James", "Larry", "Tom", "Lacy"};
for( String name : names ) {
System.out.print( name );
System.out.print(",");
}
}
}
2.去掉重复的字符
- 输入一个字符串,输出将其中重复出现的字符去掉后的字符串
方法2:String是java中的常用类,使用其自带的indexOf(C++中是find)(也可以是contains)方法,查询字符串中是否有新字符,有返回下标,没有返回-1。代码如下
public class Main{
public static void main(String args[]){
Scanner sc=new Scanner(System.in);
String s1=sc.next();
char c[]=s1.toCharArray();
String s2="";
for(int i=0;i<s1.length();i++){
if(s2.indexOf(c[i])==-1)//s2.indexOf(s1.toCharArray()[i])
s2+=c[i];
}
System.out.print(s2);
}
}
3.统计一个子串在整串中出现的次数
- 编写一个程序,统计一个子串在整串中出现的次数
解析: 本题与上面那道题几乎相同,最直接的方法也是循环比较。(不演示)
方法2:使用String类中自带的replace方法,将所有子串转为空,计算长度变化之差再除以子串长度即可。代码如下:
public class Main{
public static void main(String args[]){
Scanner sc=new Scanner(System.in);
String s1=sc.next();
String s2=sc.next();
int origin_length=s1.length();
s1=s1.replace(s2,"");//第一个参数是想替换的字符串,第二个是替换成什么
int last_length=s1.length();
System.out.print((origin_length-last_length)/s2.length());
}
}
4. 从一个字符串中移除包含在另一个字符串中的字符
- 从一个字符串中移除包含在另一个字符串中的字符。输入两行字符串,将第一行字符串中包括第二行字符串中的所有字母去除。输出去除后保留的字符串。
解析: 由题意,最直接的方法应该是外层循环是被剔除的字符串,内层循环是决定剔除字符的字符串,比较可得答案。
方法2:使用String类的replace方法,结合java的正则表达式的知识点,即可得到解决方案。代码如下:
public class Main{
public static void main(String args[]){
Scanner sc=new Scanner(System.in);
String s1=sc.nextLine();
String s2=sc.nextLine();
s1=s1.replaceAll("["+s2+"]","");
System.out.print(s1);
}
}
此处的"["+s2+"]"代表:s2中的任意字符。
5.Prime Numbers
- Your program reads two natural numbers m and n in, and prints out the sum of all prime numbers within [m,n], where 1⩽m≤n⩽10
解析:这道题是到典型的求素数的题,而求素数一般遇到的最大的问题就是运行超时
以下是几种改进方案:
- 循环除以一个小于等于它本身开根号的数。原理很简单,合数能拆成非1的两个数,而当你判定超过本身开根号时,再增大这个数,它的对应因数会减小,就相当于之前的操作反过来。(这种操作起到的效果很有限)
- 只判断奇数,偶数除2外必定是素数(此操作可用于判断1~n之间有多少素数的题型)
- 最后一种,也是我认为最有效的方法,类似于简单筛选法,原理是:一个素数的倍数一定不是素数,代码如下:
public class Main{
public static void main(String args[]){
Scanner sc=new Scanner(System.in);
int input=sc.nextInt();
int count=0;
ArrayList<Boolean> isPrime=new ArrayList<Boolean>();
for(int i=0;i<=input;i++)
isPrime.add(true);
//初始值设定
isPrime.set(0,false);
isPrime.set(1,false);
for(int i=2;i*i<=input;i++){
if(isPrime.get(i)==true){
for(int j=2*i;j<=input;j+=i){
isPrime.set(j,false);
}
}
}
for(boolean x:isPrime){
if(x==true)
count++;
}
System.out.println(count);
}
}
这个方法嘎嘎快:kissing:
训练集二
主要内容:巩固java的基础语法,控制语句,数组等内容。
1.判断三角形类型
- 输入三角形三条边,判断该三角形为什么类型的三角形。
解析:这道题是很典型的练习分支语句的例题,不过其中有种情况有点小坑。
等腰直角三角形:判断等腰直角三角形需要用到勾股定理,而勾股定理涉及到开更号问题,计算机不能精确计算。
解决方法:设置误差判断,当误差足够小,则可判定该三角形就是等腰直角三角形。判断等腰直角三角形代码如下:
for(i=0;i<3;i++){
for(j=i+1;j<3;j++)
if(side[i]==side[j]&&Math.abs(side[i]*side[i]+side[j]*side[j]-side[3-i-j]*side[3-i-j])<10e-6){
System.out.print("Isosceles right-angled triangle");
return;
}
}
2.二进制数值提取
- 在一个字符串中提取出其中的二进制数值序列。
输入格式:
一个由0、1构成的序列,以-1为结束符,非0、1字符视为正常输入,但忽略不计,未包含结束符的序列视为非法输入。例如:abc00aj014421-1
输出格式:
将输入的序列去掉非0、1字符以及结尾符的数据内容,
注:结束符-1之后的0\1字符忽略不计。
例如:00011。
解析:使用indexOf方法查询-1,在-1出现前循环比较字符,出现0 1留下。代码如下:
public class Main{
public static void main(String args[]){
Scanner sc=new Scanner(System.in);
String s=sc.nextLine();
int k=s.indexOf("-1");//类似C++里的string find
if(k==-1){
System.out.print("Wrong Format");
return;
}
for(int i=0;i<s.length();i++){
if(s.charAt(i)=='0'&&i<k)
System.out.print(0);
else if(s.charAt(i)=='1'&&i<k)
System.out.print(1);
}
}
}
训练集三
主要内容:介绍 Java 面向对象编程的基础知识,包括类、对象、封装等,熟悉类的设计与规范书写
1.定义日期类
- 定义一个类Date,包含三个私有属性年(year)、月(month)、日(day),均为整型数,其中:年份的合法取值范围为[1900,2000] ,月份合法取值范围为[1,12] ,日期合法取值范围为[1,31] 。
解析:这道题目本身不难,重点在于如何去规范书写一个类
先搬上我自己的代码吧。如下:
import java.util.*;
public class Main{
public static void main(String []args){
Scanner sc=new Scanner(System.in);
Date myDate=new Date();
int year=sc.nextInt();
myDate.setYear(year);
myDate.isLeapYear(year);
int month=sc.nextInt();
myDate.setMonth(month);
int day=sc.nextInt();
myDate.setDay(day);
if(myDate.checkInputValidity()==false){
return;
}
else{
myDate.getNextDate();
}
}
}
class Date{
private int year;
private int month;
private int day;
int [] mon_maxnum={0,31,28,31,30,31,30,31,31,30,31,30,31};
public void Date(int year,int month,int day){
this.year=year;
this.day=day;
this.month=month;
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
public int getMonth() {
return month;
}
public void setMonth(int month) {
this.month = month;
}
public int getDay() {
return day;
}
public void setDay(int day) {
this.day = day;
}
public boolean isLeapYear(int year){
if((year%400==0)||(year%100!=0&&year%4==0)){
mon_maxnum[2]++;
return true;
}
return false;
}
public boolean checkInputValidity(){
if(year<1900||year>2000||month>12||month<1||day<1||day>mon_maxnum[month]){
System.out.printf("Date Format is Wrong");
return false;
}
return true;
}
public void getNextDate(){
if(month==12&&day==31)
System.out.printf("Next day is:%d-%d-%d",++year,1,1);
else{
if(day==mon_maxnum[month]){
System.out.printf("Next day is:%d-%d-%d",year,++month,1);
}
else{
System.out.printf("Next day is:%d-%d-%d",year,month,++day);
}
}
}
}
这段代码虽然够解出这道题,但其中类的设计方面存在着不少逻辑漏洞。
- 最大的问题在于:没有恪守单一职责原理,代码中的许多方法如:isLeapYear,checkInputValidity,geNextDate方法,他们都没有做到单一职责,有的同时具有输出功能,有的甚至能改变某些变量的值,这确实是我认为最大的问题。
- 变量命名还不是很规范,如mon_maxnum,借我老师的话评价,“这属于垃圾代码”......
典例精析
日期类设计
- 设计一个类DateUtil,该类有三个私有属性year、month、day(均为整型数),其中,year∈[1820,2020] ,month∈[1,12] ,day∈[1,31] , 除了创建该类的构造方法、属性的getter及setter方法外,需要编写如下方法:
public boolean checkInputValidity();//检测输入的年、月、日是否合法
public boolean isLeapYear(int year);//判断year是否为闰年
public DateUtil getNextNDays(int n);//取得year-month-day的下n天日期
public DateUtil getPreviousNDays(int n);//取得year-month-day的前n天日期
public boolean compareDates(DateUtil date);//比较当前日期与date的大小(先后)
public boolean equalTwoDates(DateUtil date);//判断两个日期是否相等
public int getDaysofDates(DateUtil date);//求当前日期与date之间相差的天数
public String showDate();//以“year-month-day”格式返回日期值
应用程序共测试三个功能:
- 求下n天
- 求前n天
- 求两个日期相差的天数
下面是我用powerdesigner设计的类图:
初次使用就被这软件给征服了,以后需要设计的类多的时候,感觉会特别有用。
下面将详细的介绍我的整个分析过程:
- 填补完基础的方法,如每个私有成员的getter,setter方法。
- checkInputValid方法,判断日期是否有效,若无效,主函数中可直接输出错误提示并return;减少程序运行时间
- isLeapYear方法,根据判断闰年的方法,返回相应的布尔值
- getNextNDays方法,有两种思路:
一:关键在于2月份,主要判断当天日期是否在2月份之后(或之前),在结合该年是否为闰年。
二:第二种思路就是以年份来考虑,总变化天数为n天后的日期加上今年已发生的天数,后面直接用总天数对每一年进行处理,以此类推,年份循环结束后继续月份循环,月份循环结束后,天数可以直接算出来。
相较而言,第二种思路逻辑更清晰一点 - getPreviousNDays方法,类似于getNextNDays方法,不过多赘述了。
- compareDates方法,按照年月日顺序分别比较即可。
- equalTwoDates方法,直接比较两个日期的年月日。
- getDaysofDates方法,此处沿用getNextNDays跟getPreviousDates的方法,分别对应的求出总天数,然后固定一方的年份,另一方逐渐增加(或减少)天数向其逼近至相等,然后也是年循环结束弄月循环,月循环结束,最后比较二者的天数,求出最终结果。
- showDate方法,这里值得注意的是,它返回的是String类型,所以要到主程序查看它是在什么情况下被调用的,以此写出合适的代码。
下面是我的代码:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int year = 0;
int month = 0;
int day = 0;
int choice = input.nextInt();
if (choice == 1) { // test getNextNDays method
int m = 0;
year = Integer.parseInt(input.next());
month = Integer.parseInt(input.next());
day = Integer.parseInt(input.next());
DateUtil date = new DateUtil(year, month, day);
if (!date.checkInputValidity()) {
System.out.println("Wrong Format");
System.exit(0);
}
m = input.nextInt();
if (m < 0) {
System.out.println("Wrong Format");
System.exit(0);
}
System.out.print(date.getYear() + "-" + date.getMonth() + "-" + date.getDay() + " next " + m + " days is:");
System.out.println(date.getNextNDays(m).showDate());
} else if (choice == 2) { // test getPreviousNDays method
int n = 0;
year = Integer.parseInt(input.next());
month = Integer.parseInt(input.next());
day = Integer.parseInt(input.next());
DateUtil date = new DateUtil(year, month, day);
if (!date.checkInputValidity()) {
System.out.println("Wrong Format");
System.exit(0);
}
n = input.nextInt();
if (n < 0) {
System.out.println("Wrong Format");
System.exit(0);
}
System.out.print(
date.getYear() + "-" + date.getMonth() + "-" + date.getDay() + " previous " + n + " days is:");
System.out.println(date.getPreviousNDays(n).showDate());
} else if (choice == 3) { //test getDaysofDates method
year = Integer.parseInt(input.next());
month = Integer.parseInt(input.next());
day = Integer.parseInt(input.next());
int anotherYear = Integer.parseInt(input.next());
int anotherMonth = Integer.parseInt(input.next());
int anotherDay = Integer.parseInt(input.next());
DateUtil fromDate = new DateUtil(year, month, day);
DateUtil toDate = new DateUtil(anotherYear, anotherMonth, anotherDay);
if (fromDate.checkInputValidity() && toDate.checkInputValidity()) {
System.out.println("The days between " + fromDate.showDate() +
" and " + toDate.showDate() + " are:"
+ fromDate.getDaysofDates(toDate));
} else {
System.out.println("Wrong Format");
System.exit(0);
}
}
else{
System.out.println("Wrong Format");
System.exit(0);
}
}
}
class DateUtil{
private int year,month,day;
private int yearDay=365;
int [] mon_maxday={0,31,28,31,30,31,30,31,31,30,31,30,31};
public DateUtil(){
}
public DateUtil(int year,int month ,int day){
this.year=year;
this.day=day;
this.month=month;
}
public int getYear() { return year; }
public int getMonth() {
return month;
}
public int getDay() {
return day;
}
public void setYear(int year) {
this.year = year;
}
public void setMonth(int month) {
this.month = month;
}
public void setDay(int day) {
this.day = day;
}
public boolean checkInputValidity(){//检测输入的年、月、日是否合法
if(isLeapYear(year))
mon_maxday[2]=29;
if(year<1820||year>2020||month>12||month<1||day<1||day>mon_maxday[month]){
return false;
}
return true;
}
public boolean isLeapYear(int year){//判断year是否为闰年
if((year%100!=0&&year%4==0)||(year%400==0)){
mon_maxday[2]=29;
yearDay++;
return true;
}
return false;
}
public DateUtil getNextNDays(int n){//取得year-month-day的下n天日期
while(n > 365){//为什么不先讨论n<0&&n>365:n>365时只需要对年进行讨论,再对天进行讨论
if(this.isLeapYear(year) && month<=2){//这一年是闰年且月份小于等于2
if(month==2&&day==29){
day = 1;
month = 3;
}
year++;
n=n-366;
}
else if(this.isLeapYear(year+1) && month>2){//下一年是闰年且月份大于二
year++;
n=n-366;
}
else{
year++;
n=n-365;
}
}//先对年进行讨论
while(n>0&&n<365) { //n<365时
for (int i = 0; i < n; i++) {
day++;
if (this.isLeapYear(year) && month == 2) { //与上面一样再讨论闰年和月份等于2的情况
if (day > 29) {
month++;
day = 1;
}
} else if (day > mon_maxday[month]){//月份不为2的情况
month++;
day = 1;
if (month > 12) { //最后一个月的情况
month = 1;
year++;
}
}
}
}
return this;
}
public DateUtil getPreviousNDays(int n){
DateUtil d = new DateUtil(this.year,this.month,this.day);
for (int i = 0; i < n; i++) {
if(this.isLeapYear(year))//判断是否为闰年
mon_maxday[2] = 29;
else
mon_maxday[2] = 28;
d.day--;
if(d.day == 0){//日期减为0
d.month--;
if(d.month == 0){//月份减为0
d.year--;
d.month = 12;
}
d.day = mon_maxday[d.month];
}
}
return d;
}
public boolean compareDates(DateUtil date){
if(this.year > date.getYear())
return true;
else if(this.year == date.year && this.month > date.month)
return true;
else {
if(this.month>date.month)
return true;
else if(this.month<date.month)
return false;
else{
if(this.day>date.day)
return true;
else if(this.day<date.day)
return false;
}
}
return true;
}
public boolean equalTwoDates(DateUtil date){
return this.year == date.year && this.month == date.month && this.day == date.day;
}
public int getDaysofDates(DateUtil date){
int res = 0;
if(this.equalTwoDates(date))
return 0;
if(this.compareDates(date)){
while(true){
if(isLeapYear(date.year))
mon_maxday[2] = 29;
else
mon_maxday[2] = 28;
date.day++;
if(date.day == mon_maxday[date.month]+1){
date.month++;
date.day = 1;
}
if(date.month == 13){
date.year++;
date.month=1;
}
if(date.year == year && date.month == month && date.day == day){
break;
}
res++;
}
}else{
while(true){
if(isLeapYear(year))
mon_maxday[2] = 29;
else
mon_maxday[2] = 28;
day++;
if(day == mon_maxday[month] + 1){
month++;
day = 1;
}
if(month == 13){
year++;
month = 1;
}
if(date.year == year && date.month == month && date.day == day){
break;
}
res++;
}
}
return res + 1;
}
public String showDate(){
return this.year + "-" + this.month + "-" + this.day;
}
}
踩坑心得
因为较为习惯了C/C++,换成Java很多地方容易出错,这里我列举一下自己的几种错误:
-
用String直接比较另一个String
这个错误我研究了很久,因为我想String作为一个这么常用的类,一定是能够去比较的,然后我用C++STL里的string类比过来,整的一堆红色报错。
C++里string 可以直接加string 或者 char
C++里string 可以直接进行等值判断。
java里的String比较固定(从类的定义里就可以看出Java比C++稳定)
java的String只能跟String比较(即使值相同,结果也可能是false)
java的String可以加char,也可以加String,但是不能加s[0](如果说有个String s="123")
原理深究:
因为在Java中,进行String比较时,比较的是二者的地址,尽管二者的字符串可能一模一样,但二者地址不同也会返回false.
解决方案:
使用String类的equals函数,它会比较两String的值。 -
Arraylist使用不当,直接用[ ]进行下标索引
Arraylist的功能相当于C++STL里的vector
C++里的vector可以直接用下标索引元素
C++里的vector可以进行resize操作(这个功能可以对大量数据初始化,很重要!)
Java里的ArrayList不可进行下标索引,要使用对应的get,set方法(再次体现了java的严谨)
java里的ArrayList不可以直接改变空间,只能检测元素个数(size)
解决方案:
使用Collections.ncopies方法(需要引入Collections)方法示例:
ArrayList<T> obj = new ArrayList<T>(Collections.nCopies(count,element));//把element复制count次填入ArrayList中
综述
通过这三次的java实践,我对java有了一定的认知:
从语言本身来说,java的各种语法,编程规则,类的设计,都体现了它是一门很“成熟”的语言,特别适合多人协作,这种“成熟”在市场中很重要,然而,因为它的这种特性,C++相对java更为灵活,C++可以重载运算符,而Java不行,从算法层面上说,java得记住更多的由别人定义好的方法,而C++只需要通过改动某些类中的运算符,或重载其函数,很容易就能契合算法题的需求。但C++缺点也极其明显,就是重载之后,容易出问题,合作起来非常困难。
收获:
- 掌握了java的基本语法
- 初步了解一些java关于内存,引用等原理层面的东西。
近期目标:
- 学习Java中常用的API,熟练掌握以提高效率。(非常有必要)
- 熟悉Ideal的各种操作,用ideal练习多个类的经典项目。
- 了解Java中的设计模式(在此过程中,同时要逐步理解多线程相关知识)
杂谈
- 2023/3/23,心情复杂
今年,2023年,我刚入大一(现在是下学期),22年的那个寒假,chatgpt3.5横空出世,当时的我被它强大的功能给深深震撼到了,那时的我觉得自己很幸运,对比起无数的前辈们,chatgpt在我年富力强之际出现,可以帮我写文案,可以回答我的各种问题,无疑是帮了我大忙(最重要的是节省了很多时间)。这一年的我,17岁。 - 2023年的3月14日,当我还在听反诈主题宣讲课时,chatgpt4.0出来了,我去试了一下,最大的改变是能识别图片,还有实时更新网络数据,它的迭代速度肉眼可见的快,所以我认为要不了多久,视频和音频也不在话下了(现在其实已经有别的机构用chatgpt做出了相关实验)
- 我觉得悲伤的地方在于,我高考选的,认为前景不错的专业,软件工程,可能逐渐要被时代淘汰了。参考下面的链接:
GPT的火爆,可能正在“杀死”软件工程
即使这个UP说的不全对,只信一半的话,软件工程确实面临着很大的被时代淘汰的危机。 - 所以,客观分析一下我现在的处境:相对周围人,我是个比较“卷”的人,然而,无论我怎么去卷那些经验知识,我卷的过AI吗?即使是我认为软件行业壁垒数一数二的算法领域,AI迭代发展的也必定会超过人类(现在的chatgpt4.0参加codeforces上的比赛得分能超过一半参赛者)
- 面对这样的处境,我自然而然的联想到了一句老话:“打不过就加入”。未来时代需要的肯定是结合了AI知识的程序员,不谈创造AI,至少要懂AI,能够维修AI设备里的问题,不过这样的工作壁垒也不高,因为未来的AI肯定能根据用户的指令自我修改,而这种工作需要的不是程序员,而是“AI咒语师”。不过对于现在刚入计算机专业的我而言,大方向是肯定的,我要花时间去学会使用AI,先玩明白,再去学习相关知识。
- “迷茫的时候,就先学好眼前知识”,这句话是我高中所称作的圣经。因为高中想再多也没有用,学习一定是高中阶段的最优解,唯一的使命就是高考考好。现在到了大学,我能感受到自己拥有更多的自由,能够进行更多的选择,“学好眼前知识”成为了局部最优解,更多时候需要我去思考,思考自己的未来。
写下这篇杂谈,记录一下自己作为一个新时代的见证者,等我大四的时候,可能一切就已经翻天覆地了。
标签:System,Java,int,实践,month,year,程序设计,public,day From: https://www.cnblogs.com/AI-xiong/p/17246513.html