Go Web下gin框架的模板渲染

news2025/1/12 15:57:13

〇、前言

Gin框架是一个用于构建Web应用程序的轻量级Web框架,使用Go语言开发。它具有高性能、低内存占用和快速路由匹配的特点,旨在提供简单、快速的方式来开发可扩展的Web应用程序。
Gin框架的设计目标是保持简单和易于使用,同时提供足够的灵活性和扩展性,使开发人员能够根据项目的需求进行定制。它提供了许多有用的功能,如中间件支持、路由组、请求参数解析、模板渲染等,使开发人员能够快速构建高效的Web应用程序。
以下是Gin框架的一些主要特性:

  • 快速的性能:Gin框架采用了高性能的路由引擎,使请求路由匹配变得非常快速。

  • 中间件支持:Gin框架支持中间件机制,允许你在请求处理过程中添加自定义的中间件,用于处理认证、日志记录、错误处理等功能。

  • 路由组:Gin框架允许将路由按照逻辑组织成路由组,使代码结构更清晰,并且可以为不同的路由组添加不同的中间件。

  • 请求参数解析:Gin框架提供了方便的方法来解析请求中的参数,包括查询字符串参数、表单参数、JSON参数等。

  • 模板渲染:虽然Gin框架本身不提供模板引擎,但它与多种模板引擎库(如html/template、pongo2等)集成,使你能够方便地进行模板渲染。

  • 错误处理:Gin框架提供了内置的错误处理机制,可以捕获和处理应用程序中的错误,并返回适当的错误响应。

总体而言,Gin框架是一个简单、轻量级但功能强大的Web框架,非常适合构建高性能、可扩展的Web应用程序。它在Go语言社区中得到广泛的认可,并被许多开发人员用于构建各种类型的Web应用程序。

一、html/template

html/template是Go语言标准库中的一个包,用于生成和渲染HTML模板。它提供了一种安全且灵活的方式来生成HTML输出,支持模板继承、变量替换、条件语句、循环结构等功能。

html/template包的主要目标是防止常见的Web安全漏洞,如跨站脚本攻击(XSS)。它通过自动进行HTML转义和编码来确保生成的HTML是安全的,并防止恶意用户注入恶意代码。

使用html/template包可以将动态数据与静态HTML模板分离,使代码更易于维护和重用。你可以定义模板文件,然后将数据传递给模板进行渲染,最后生成最终的HTML输出。

(一)初次渲染

先创建一个名为 hello.tmpl的文件:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <title>Hello</title>
</head>
<body>
<p>hello, {{.}}</p>
</body>
</html>

这里的{{.}}中的.就代表了我们要填充的东东,接着创建我们的main.go函数:

package main

import (
	"fmt"
	"html/template"
	"net/http"
)

func sayHello(w http.ResponseWriter, r *http.Request) {
	// 请勿刻舟求剑,用绝对地址
	t, err := template.ParseFiles("/Users/luliang/GoLand/gin_practice/chap1/hello.tmpl")
	if err != nil {
		fmt.Println("http server failed:%V", err)
		return
	}
	// 渲染模板
	err = t.Execute(w, "小王子!")
	if err != nil {
		fmt.Println("http server failed:%V", err)
		return
	}
}
func main() {
	http.HandleFunc("/hello", sayHello)
	err := http.ListenAndServe(":9000", nil)
	if err != nil {
		fmt.Println("http server failed:%V", err)
		return
	}
}

使用html/template进行HTML模板渲染的一般步骤如下:

  • 定义模板:
    首先,你需要定义HTML模板。可以在代码中直接定义模板字符串,也可以将模板保存在独立的文件中。比如我们创建了模板:hello.tmpl

  • 创建模板对象:
    使用template.New()函数创建一个模板对象。你可以选择为模板对象指定一个名称,以便在渲染过程中引用它。

  • 解析模板:
    使用模板对象的Parse()或ParseFiles()方法解析模板内容。如果模板内容保存在单独的文件中,可以使用ParseFiles()方法解析文件内容并关联到模板对象。

  • 渲染模板:
    创建一个用于存储模板数据的数据结构,并将数据传递给模板对象的Execute()方法。该方法将渲染模板并将结果写入指定的输出位置(如os.Stdout或http.ResponseWriter)。

在浏览器中输入:127.0.0.0:9000/hello就可以看到结果:hello, 小王子!

(二)传入其它数据进行渲染

上次渲染,我们用的是这一句:err = t.Execute(w, "小王子!"),可以看到我们传入了一个字符串而已。这次我们打算传点稍微不同的其它数据。继续编写模板test.tmpl:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <title>Hello</title>
</head>
<body>
<p>姓名: {{.Name}}</p>
<p>年龄: {{.Age}}</p>
<p>性别: {{.Gander}}</p>
</body>
</html>

可以看到,我们的模板稍微复杂起来了!继续编写 main.go

package main

import (
	"fmt"
	"html/template"
	"net/http"
)

type User struct {
	Name   string
	Gander string // 首字母是否大小写,作为是否对外暴露的标识
	Age    int
}

func sayHello(w http.ResponseWriter, r *http.Request) {
	// 定义模板
	u1 := User{
		Name:   "小王子",
		Gander: "男",
		Age:    19,
	}
	// 解析模板
	t, err := template.ParseFiles("/Users/luliang/GoLand/gin_practice/chap2/test.tmpl")
	if err != nil {
		fmt.Println("ParseFiles failed:%V", err)
		return
	}
	err = t.Execute(w, u1)
	if err != nil {
		return
	}

}
func main() {
	http.HandleFunc("/hello", sayHello)
	err := http.ListenAndServe(":9000", nil)
	if err != nil {
		fmt.Println("http server failed:%V", err)
		return
	}

}

可以看到我们在里面定义了一个结构类型 User,传入了一个 User 对象 u1:err = t.Execute(w, u1)

type User struct {
	Name   string
	Gander string // 首字母是否大小写,作为是否对外暴露的标识
	Age    int
}

赶紧看看运行结果,可以看到结果符合预期:

姓名: 小王子
年龄: 19
性别: 男

(三)定义函数参与渲染

定义我们的模板:f.tmpl:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <title>hello</title>
</head>
<body>
{{ kua . }}
</body>
</html>

可以看到,我们的模板里面有一个函数名字叫做 kua,没错,这就是我们要的函数。

编写main.go:

package main
import (
	"fmt"
	"html/template"
	"net/http"
)
func f(w http.ResponseWriter, r *http.Request) {
	// 定义模板
	k := func(name string) (string, error) {
		return name + "太棒了!", nil
	}
	t := template.New("f.tmpl")
	t.Funcs(template.FuncMap{
		"kua": k,
	})
	// 解析模板
	_, err := t.ParseFiles("/Users/luliang/GoLand/gin_practice/chap3/f.tmpl")
	if err != nil {
		return
	}
	// 渲染模板
	err = t.Execute(w, "小王子")
	if err != nil {
		return
	}
}
func main() {
	http.HandleFunc("/hello", f)
	err := http.ListenAndServe(":9002", nil)
	if err != nil {
		fmt.Println("http server failed:%V", err)
		return
	}

}

可以看到,我用了一个关键语句将函数kua 关联到了模板:

	t.Funcs(template.FuncMap{
		"kua": k,
	})

点击运行,可以看到结果:小王子太棒了!

(四)c.HTML() 渲染

在Gin框架中,可以使用c.HTML()方法来进行HTML模板渲染。该方法接收HTTP状态码、模板名称和渲染所需的数据作为参数。

编写模板index.tmpl:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>posts/index</title>
</head>
<body>
{{.title}}
</body>
</html>

可以看到我们只需渲染传入对象的 title 值,编写 main.go:

package main

import (
	"github.com/gin-gonic/gin"
	"net/http"
)

func main() {
	r := gin.Default()
	r.LoadHTMLFiles("/Users/luliang/GoLand/gin_practice/chap4/index.tmpl")
	r.GET("/index", func(c *gin.Context) {
		c.HTML(http.StatusOK, "index.tmpl", gin.H{
			"title": "你好,前端真是太有意思了!",
		})
	})
	r.Run(":9091")
}

首先,创建一个 gin.default()路由对象,然后给该对象载入已经写好的模板文件,之后就可以用 GET 函数进行请求了。
先看看 GET 是什么东西:

// GET is a shortcut for router.Handle("GET", path, handlers).
func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes {
	return group.handle(http.MethodGet, relativePath, handlers)
}

它说,GET 是router.Handle("GET", path, handlers)的一个捷径。再看看router.Handle("GET", path, handlers)是什么东西:

func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {
	absolutePath := group.calculateAbsolutePath(relativePath)
	handlers = group.combineHandlers(handlers)
	group.engine.addRoute(httpMethod, absolutePath, handlers)
	return group.returnObj()
}

// Handle registers a new request handle and middleware with the given path and method.
// The last handler should be the real handler, the other ones should be middleware that can and should be shared among different routes.
// See the example code in GitHub.
//
// For GET, POST, PUT, PATCH and DELETE requests the respective shortcut
// functions can be used.
//
// This function is intended for bulk loading and to allow the usage of less
// frequently used, non-standardized or custom methods (e.g. for internal
// communication with a proxy).

原来就是做了一下中间处理,注册了一个新的请求句柄。它还说GET, POST, PUT, PATCH、DELETE 都有类似的捷径。之后在 handlers中放一个匿名函数:

func(c *gin.Context) {
		c.HTML(http.StatusOK, "index.tmpl", gin.H{
			"title": "你好,前端真是太有意思了!",
		})
	}

这个函数就是用于渲染的函数。猜测c.HTML()依然是一个 shortcut:

// HTML renders the HTTP template specified by its file name.
// It also updates the HTTP code and sets the Content-Type as "text/html".
// See http://golang.org/doc/articles/wiki/
func (c *Context) HTML(code int, name string, obj any) {
	instance := c.engine.HTMLRender.Instance(name, obj)
	c.Render(code, instance)
}

可以看到真正干活的还是 Render()

点击运行,结果为:你好,前端真是太有意思了!

(五)从多个模板中选择一个进行渲染

如果要渲染多个文件,该如何操作?比如我们通过输入不同的网站,服务器这时只需要选择不同的模板进行渲染就好。在这里创建了两个模板文件,users/index.tmpl:

{{define "users/index.tmpl"}}
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>users/index</title>
    </head>
    <body>
    {{.title}}
    </body>
    </html>
{{end}}

posts/index.tmpl:

{{define "posts/index.tmpl"}}
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>posts/index</title>
    </head>
    <body>
    {{.title}}
    </body>
    </html>
{{end}}

这里使用了{{define "posts/index.tmpl"}}...{{end}}结构,对模板文件进行了命名。
编写main.go:

package main

import (
	"github.com/gin-gonic/gin"
	"html/template"
	"net/http"
)

func main() {
	r := gin.Default()
	
	r.LoadHTMLGlob("/Users/luliang/GoLand/gin_practice/chap5/templates/**/*")
	r.GET("/posts/index", func(c *gin.Context) {
		c.HTML(http.StatusOK, "posts/index.tmpl", gin.H{
			"title": "posts/index.tmpl",
		})
	})
	r.GET("/users/index", func(c *gin.Context) {
		c.HTML(http.StatusOK, "users/index.tmpl", gin.H{
			"title": "送你到百度!",
		})
	})
	r.Run(":9091")
}

我们在在浏览器地址栏输入http://127.0.0.1:9091/users/index可以得到:送你到百度!;而输入http://127.0.0.1:9091/posts/index可以得到:posts/index.tmpl

(六)加点东西,使得事情朝着有意思的方向进行!

我们为了使得网页画面更有趣,这里使用了多个文件,分别是index.css、index.js。编写:index.css:

body {
    background-color: #00a7d0;
}

没错只有一句话。接下来编写 index.js:

alert("Hello, Web!")

没错,也只有一句话。然后,我们把 css、js 文件引入到 index.tmpl中:

{{define "users/index.tmpl"}}
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <link rel="stylesheet" href="/xxx/index.css">
        <title>users/index</title>
    </head>
    <body>
    {{.title}}

    <script src="/xxx/index.js"></script>
    </body>
    </html>
{{end}}

接下来写main.go:

package main

import (
	"github.com/gin-gonic/gin"
	"html/template"
	"net/http"
)

func main() {
	r := gin.Default()
	// 加载静态文件
	r.Static("/xxx", "/Users/luliang/GoLand/gin_practice/chap5/statics")
	
	r.LoadHTMLGlob("/Users/luliang/GoLand/gin_practice/chap5/templates/**/*")
	r.GET("/posts/index", func(c *gin.Context) {
		c.HTML(http.StatusOK, "posts/index.tmpl", gin.H{
			"title": "posts/index.tmpl",
		})
	})
	r.GET("/users/index", func(c *gin.Context) {
		c.HTML(http.StatusOK, "users/index.tmpl", gin.H{
			"title": "送你到百度!",
		})
	})

	r.Run(":9091")
}

因为我们要把 css、js 文件加载进去,因此要用 r.Static()函数把放置静态文件的目录加入进去。为了复用,第一个参数“/xxx”的意思是,只要index.tmpl文件中存在“/xxx”字段就直接指到我们设置的目录。
运行结果:首先 js 文件会首先渲染:
在这里插入图片描述
之后,css 文件会渲染:在这里插入图片描述

可以看到这里出现了一个超链接,那是因为我们在这个字符后面插入了一个函数:

// 添加自定义函数
	r.SetFuncMap(template.FuncMap{
		"safe": func(str string) template.HTML {
			return template.HTML(str)
		},
	})

配合 index.tmpl,就可以了。
在这里插入图片描述

三、利用已有模板进行部署

通过以上的例子,终于学会了在模板中插入大量的css、js 进行渲染了!
首先找到某个网站,比如站长之家,下载一个模板:在这里插入图片描述
选择第一个,下载之后解压:在这里插入图片描述

static 外面的 index.html有用,其它都可以忽略。把 static 里面的文件夹复制到我们的工作目录:在这里插入图片描述
同时,把 index.html文件复制到 posts(无所谓,强迫症而已),就可以使用了。

	r.GET("/home", func(c *gin.Context) {
		c.HTML(http.StatusOK, "index.html", nil)
	})

这样,在地址栏输入:http://127.0.0.1:9091/home,就可以看到效果了:
在这里插入图片描述
渲染的相当完美!这里会存在一些小细节,比如在 index.html文件中需要把 css
、js文件的地址进行变更,变更到你放置这些文件的地址:
在这里插入图片描述
代码附上main.go

package main

import (
	"github.com/gin-gonic/gin"
	"html/template"
	"net/http"
)

func main() {
	r := gin.Default()
	// 加载静态文件
	r.Static("/xxx", "/Users/luliang/GoLand/gin_practice/chap5/statics")
	// 添加自定义函数
	r.SetFuncMap(template.FuncMap{
		"safe": func(str string) template.HTML {
			return template.HTML(str)
		},
	})
	r.LoadHTMLGlob("/Users/luliang/GoLand/gin_practice/chap5/templates/**/*")
	r.GET("/posts/index", func(c *gin.Context) {
		c.HTML(http.StatusOK, "posts/index.tmpl", gin.H{
			"title": "posts/index.tmpl",
		})
	})
	r.GET("/users/index", func(c *gin.Context) {
		c.HTML(http.StatusOK, "users/index.tmpl", gin.H{
			"title": "<a href='https://www.baidu.com'>送你到百度!</a>",
		})
	})
	r.GET("/home", func(c *gin.Context) {
		c.HTML(http.StatusOK, "index.html", nil)
	})

	r.Run(":9091")
}

这意味着,我们以后要想写网页,根本不需要进行大量的无意义的编程,利用 ChatGPT,我们可以写出大量的优秀的css、js 网页,我们要做的只是进行适量的改动,这将极大地丰富我们的创造力!

四、总结

本文从简单到难,对Go Web中 gin 框架下的模板渲染进行了简单的阐述。

全文完,感谢阅读!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/561034.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

利用ChatGPT来学习Power BI

学习Power BI&#xff0c;或者说学习微软的相关产品的时候&#xff0c;最讨厌的就是阅读微软的官方文档&#xff0c;写的真的太硬了&#xff0c;有时候实时是啃不动&#xff0c;只能说不愧是巨硬。 但是&#xff0c;我们现在有AI帮忙了啊&#xff0c;ChatGPT3都通过了谷歌L3工…

达利欧《原则》拆书笔记(二)

什么是原则&#xff1f; 原则是应对现实、实现你人生愿望的方法。 假如没有原则&#xff0c;我们将被迫逐一考虑多种类型的事情&#xff0c;主动去应对&#xff0c;就像第一次经历这些事。相反&#xff0c;假如我们把每件事都看作“同一类型事物的又一个表现”&#xff0c;以…

【SpringCloud组件——Feign(远程调用)】

前言&#xff1a; 我们在使用Nacos和Eureka的时候都需要使用远程调用开关RestTemplate发送http请求&#xff0c;但是这种方式在代码编写层面太不优雅了&#xff0c;因此我们可以采用Feign来代替RestTemplate发送http请求。 注&#xff1a;此小节同样使用订单系统和用户系统作…

VXLAN技术了解

VXLAN是使用隧道技术的封装协议&#xff0c;常用于在物理层之上创建overlay网络&#xff0c;赋能虚拟网络。同时支持数据中心网络的虚拟化&#xff0c;并通过提供必要的分段满足多租户的需求。 优势在于 可伸缩性和灵活性&#xff1a;理论上可以使用1600万xlans&#xff0c;但…

读书笔记——《when breath becomes air》《超越自卑》

为啥要两本书一起写读后感&#xff1f; 读完这两本书本来应该分开来写点东西的&#xff0c;不过我认为这两本书应该写不了太多内容。虽然我也看了几本英文原著&#xff08;也写了点东西&#xff09;&#xff0c;但是我明显低估了《when breath becomes air》的难度&#xff0c…

SpringBoot学习之集成JWT(二十八)

一、什么是JWT WT (全称:Json Web Token)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为 JSON 对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。 比如我们常见的登录流程如下: 流程描述一下: 用户使用账号、密码登…

机器学习-3 K最近邻算法

K最近邻算法 算法概述分类什么是分类&#xff1f;分类需要什么&#xff1f; k近邻&#xff08;KNN&#xff09;分类 KNN算法关键问题k近邻模型的两个关键问题相似性度量——欧氏距离K值的选取 KNN算法流程算法原理算法步骤 数据标准化离差标准化数据标准差标准化数据小数定标标…

文献阅读-A Survey on Transfer Learning

期刊会议&#xff1a;IEEE TRANSACTIONS ON KNOWLEDGE AND DATA ENGINEERING 时间&#xff1a;2010 级别&#xff1a;CCF A 1、为什么需要迁移 许多机器学习和数据挖掘算法的一个主要假设是&#xff0c;训练数据和未来数据必须在相同的特征空间中&#xff0c;并且具有相同…

CSS的z-index属性

之前以为z-index越大&#xff0c;它的层级就越高&#xff0c;从我遇到的bug来看很显然不是这样。重新回顾一下z-index的知识&#xff1a; 1、z-index只对定义了position属性的&#xff0c;且属性值是非static元素有效。它的默认值是auto&#xff0c;auto的意思是游览器自动进行…

配置工具篇(tmux+ranger+lazygit)

Tmux 简介 官网解释&#xff1a;tmux是一个终端多路复用器&#xff1a;它允许在单个屏幕上创建、访问和控制多个终端。tmux可以从屏幕上分离出来&#xff0c;继续在后台运行&#xff0c;然后再重新连接。 此版本在Open BSD、Free BSD、Net BSD、Linux、mac OS和Solaris上运行…

数据结构 --- 堆

1、堆的基本概念 之前在学习优先级队列的时候&#xff0c; 学习到了堆的概念&#xff0c;现在重新回忆一下&#xff1a; 堆在逻辑上&#xff0c;是一颗完全二叉树堆在物理上&#xff0c;是存储在数组中的任意根节点值>子树节点值&#xff0c;叫做大顶堆。任意根节点值<…

学习RabbitMQ高级特性

目标&#xff1a; 了解熟悉RabbitMQ的高级特性 学习步骤&#xff1a; 高级特性主要分为以下几点, 官网介绍 1、消息可靠性投递 【confirm 确认模式、return 退回模式】 2、Consumer ACK 【acknowledge】 3、消费端限流 【prefetch】 4、TTL过期时间 【time to live】 5、死信队…

SQL注入 - Part 3(带外)

1、DNSLog注入 &#xff08;一种注入新思路&#xff09; 可以看到DNS的解析日志中包含了用户名。 基于此原理&#xff0c;可以手工构造注入点&#xff0c;让DNSlog显示库名、表名等&#xff0c;也可以使用自动化脚本Dnslogsqlinj进行获取。 2、SQL注入的防御 基于关键字&…

rollup打包react组件

这次主要简单实现用rollup打包react组件&#xff0c;组件的话简单写了一个弹窗组件&#xff0c;效果如下&#xff1a; 点击打开弹框&#xff0c;点击关闭按钮关闭弹框 首先创建react项目&#xff0c;这边还是用mfex-project脚手架创建 mfex-project create react-demo 然后编…

Linux·深入理解IO复用技术之epoll

目录 1.写在前面 2.初识复用技术和IO复用 3. Linux的IO复用工具概览 4. 初识epoll 5. epoll的底层细节 6.LT模式和ET模式 7.epoll的惊群问题 1.写在前面 今天一起来学习一下高并发实现的的重要基础&#xff1a;I/O复用技术 & epoll原理。 通过本文你将了解到以下内容…

【JavaScript】ES6,Proxy,Reflect,Promise,生成器,async/await

❤️ Author&#xff1a; 老九 ☕️ 个人博客&#xff1a;老九的CSDN博客 &#x1f64f; 个人名言&#xff1a;不可控之事 乐观面对 &#x1f60d; 系列专栏&#xff1a; 文章目录 ES6模板字符串&#xff0c;标签模板字符串函数的默认参数函数的剩余参数剩余参数和arguments有什…

为什么要学习C++软件调试技术?掌握调试技术都有哪些好处?

目录 1、为什么要学习C软件调试技术&#xff1f; 1.1、IDE调试手段虽必不可少&#xff0c;但还不够 1.2、通过查看日志和代码去排查异常崩溃问题&#xff0c;费时费力&#xff0c;很难定位问 1.3、有的问题很难复现&#xff0c;可能只在客户的环境才能复现 1.4、开发联调工…

使用git远程上传github

如果你不是很熟悉git&#xff0c;在使用git前请先对你的项目进行备份 进入本地文件目录&#xff0c;打开git的cmd界面&#xff08;Git Bash Here&#xff09;如果当面目录使用过git&#xff0c;有.git隐藏文件&#xff0c;则跳过第二步&#xff0c;没有则输入以下命令 git ini…

零基础如何自学网络安全?

第一阶段&#xff1a;学习一种或几种编程语言。 网络安全也属于计算机范畴&#xff0c;涉及到IT行业的&#xff0c;编程语言是不可难免的。 《Head First Python(第2版)》 作为一种高级编程语言&#xff0c;Python越来越受到网络专家的欢迎。它之所以吸引人&#xff0c;主要…

【Java 28岁了】一个有趣的例子,再推荐一些经典好书(文末惊喜福利)

文章目录 1 写在前面2 C语言与Java语言的互相调用2.1 C语言调用Java语言2.2 Java语言调用C语言 3 友情推荐4 更多分享 1 写在前面 众所周知&#xff0c;C语言和Java语言是两种不同的编程语言&#xff0c;它们的关系可以描述为Java语言是在C语言的基础上发展而来的一种高级编程…